是否可以在 C# 中创建具有自定义值或模板值的谓词

Is it possible to create in C# a predicate with a custom values, or templated values

我是 C# 的新手,有 C++ 背景。我一直在寻找列表搜索和 Find.

的用法

我正在尝试定义一个接受参数的函数,returns 是列表中的一个条目。例如

class Product
{
    public string id;
    public string description;
}

public List<Product> products = new List<Product>();
// Add some items to the list

我的问题是,我可以使用 Predicates/Funcs

等 C# 功能来打开以下自定义“查找”功能吗
Product GetProductByID(string id)
{
    foreach (Product p in products)
    {
        if (p.id == id)
        {
            return p;
        }
    }
}

我尝试过使用谓词,但我似乎无法找到将值输入函数以进行布尔表达式检查的方法。我了解如何编写谓词来查找列表中的特定项,但不是可以传入的动态项。

理想情况下,我想更进一步并对其进行模板化,以便 Predicate 可用于任何具有相似结构的项目列表。

即如果我们也有

class Employee
{
    public string id;
    public int age;
}

public List<Employee> employees = new List<Employee>();
// Add some items to the list

使用相同定义的谓词真的很酷,但是在这个完全不同类型的列表上。即类似

// psudo
Predicate<T, string> predicate = return <T>.id == id;

它之所以有效,是因为 T 总是具有一个名为 id

的字符串成员的类型

我希望一切都有意义。

I understand how I would write a Predicate to find a speciic item in a list, but not a dynamic one that could be passed in.

这就是 lambda 表达式的用武之地,它允许您创建一个委托类型的实例来捕获范围内的变量。例如:

public Predicate<Product> CreateProductPredicateForId(string id)
{
    return product => product.id == id;
}

现在要使 通用 ,您需要有一个指定 Id 属性 的接口。然后你可以写一个泛型方法。例如:

public interface IEntity
{
    string Id { get; }
}
...

public Predicate<T> CreatePredicateForId<T>(string id) where T : IEntity
{
    return entity => entity.Id == id;
}

(注意:在整个过程中我都在使用 Func<T, bool> - 但完全相同的逻辑适用于 Predicate<T> - 它们具有相同的签名,但你往往会看到更多的前者而不是这些天是后者;也就是说:List<T> 使用后者)

泛型支持这一点,但通过变体规则;您必须声明以下形式的接口:

interface IHazId
{
    int Id { get; }
}
class Foo : IHazId // your types need to implement it, too!
{
    public int Id { get; set; }
}

然后就可以通用使用了:

    void SomeCallingCode()
    {
        // get the predicate, however
        int id = 42;
        Func<IHazId, bool> predicate = obj => obj.Id == id;

        // use it
        SomeTypeSpecificMethod(predicate);
    }
    void SomeTypeSpecificMethod(Func<Foo, bool> predicate)
        => throw new NotImplementedException("not shown");

与 compiler/runtime 允许 Func<IFoo, bool> 满足特定类型的 Func<Foo, bool> 要求,因为它知道所有 Foo 都是 IHazId。您还可以使用具有约束的泛型来执行此操作:

    void SomeCallingCode()
    {
        // get the predicate, however
        int id = 42;
        Func<IHazId, bool> predicate = obj => obj.Id == id;

        // use it
        SomeGenericMethod<Foo>(predicate);
    }
    void SomeGenericMethod<T>(Func<T, bool> predicate)
        where T : IHazId
        => throw new NotImplementedException("not shown");

然而,实际上,这很少有用,因为在大多数情况下您已经知道类型,并且只想做:

int id = 42;
SomeTypeSpecificMethod(x => x.Id == id);

另请注意,允许您将 Func<IHazId, bool> 视为 Func<SomeType, bool> 的差异规则仅适用于 reference type SomeType - 这是说:class,而不是 struct.

您可以使用 LINQ

Product myProduct = products.Where(p => p.Id == id).FirstOrDefault();

您需要在文件顶部 using System.Linq; 才能使用它。

Product GetProductByID(string id)
{
    foreach (Product p in products)
    {
        if (p.id == id)
        {
            return p;
        }
    }
}

这可以替换为

products.FirstOrDefault(p => p.Id == id)

你问题的第二部分可以通过继承轻松解决,比如

public class ProductA
{
    public string Id { get; set; }
}

public class ProductB : ProductA
{
    
}

List<ProductA> a = new List<ProductA>();
List<ProductB> b = new List<ProductB>();

Func<ProductA, bool> findFunction = (p) => p.Id == id;

var p = a.FirstOrDefault(findFunction);
p = b.FirstOrDefault(findFunction);