【问题标题】:Lambda works in FindAll, but not when using it as an Func (or Expression)Lambda 在 FindAll 中有效,但在将其用作 Func(或表达式)时无效
【发布时间】:2015-10-31 15:31:46
【问题描述】:

下面的代码不会编译:

Func<Person, bool> theLambda = (p) => p.Year >= 1992;

foreach (Person pers in PersonList.FindAll(theLambda))
{
    Console.WriteLine(pers.Name);
}

public class Person
{
    public string Name { get; set; }
    public int Year { get; set; }

    public Person(string Name, int Year )
    {
        this.Name = Name; this.Year = Year;
    }
}  

但是,如果我直接用 lambda 替换变量“theLambda”,那么它就可以正常工作。这里发生了什么? (温柔点,我是新手)。非常感谢您!
(1) 我阅读了错误信息,但它对我没有任何意义。
(2) 是的,我可以通过使用 compile() 关键字使其与 Predicate 一起使用,但这不是这里的问题。

编辑:为什么会有人对此投反对票?这个问题并没有那么糟糕,因为问题域确实不具有逻辑性质。真的是人。

【问题讨论】:

  • 错误信息是什么?
  • 可能是因为您使用的类型不正确。请改用Predicate&lt;Person&gt;

标签: c# lambda expression predicate findall


【解决方案1】:

它之所以有效,是因为如果您将 lambda 内联声明,编译器会隐式为其分配正确的类型,即Predicate&lt;Person&gt;。您不必明确告诉编译器 lambda 类型,因为它已经知道如果您在 List&lt;Person&gt; 上调用 FindAll,它应该采用 Person 并返回 bool

foreach (Person pers in PersonList.FindAll(p => p.Year >= 1992))
{
    Console.WriteLine(pers.Name);
}

您也可以使用 Enumerable.Where - 具有相同功能的 LINQ 方法,使其更具可读性:

foreach (Person pers in PersonList.Where(p => p.Year >= 1992))
{
    Console.WriteLine(pers.Name);
}

来自msdn

在编写 lambdas 时,您通常不必为 输入参数,因为编译器可以根据 lambda 主体、参数的委托类型和其他因素 在 C# 语言规范中描述。对于大多数标准 查询运算符,第一个输入是元素的类型 源序列。因此,如果您查询的是IEnumerable&lt;Customer&gt;,那么 输入变量被推断为Customer 对象

令人困惑的部分是 Predicate 在逻辑上是 Func,它接受某种类型的对象 T 并返回 bool,但由于某种原因,这种类型不起作用,您必须使用Predicate&lt;T&gt;。内联声明 lambda 函数可以避免这种混淆,因为您只需编写 lambda 主体并让编译器自行推断类型。

【讨论】:

  • @Praveen 你绝对解决了我不明白的问题域!所以内联有效,你解释了原因。即使我传递了一个 lambda,而不是一个谓词,编译器也知道该做什么。所以这是一个编译器的事情。明白了!所以当我这样做时: Func theLambda = (p) => p.Year >= 1992; 或者这个 Expression> theLambda = ( p) => p.Year >= 1992 编译器突然不行了?是这样吗?
  • @Valmont - Lambda 只是一个匿名函数的名称,Predicate,或Func,或Action 是此匿名函数的类型。 lambdas 的全部意义在于,当编译器从您使用它们的上下文中知道该类型时,您不必显式声明它们的类型 - 就像在这个例子中一样。在 msdn 上查看这篇文章 - msdn.microsoft.com/en-us/library/bb397687.aspx
  • 但由于某种原因,以这种方式声明 lambda 不起作用,您必须使用 Predicate<...snip comment...> 完全正确。事实上,谓词也是一个返回布尔值的表达式 (!)!这可能是因为 predicate 根本不是谓词,而是不同的东西。因此,为什么必须先编译它才能将其从表达式转换?在这种情况下,谓词一词令人困惑,应该称为 PredicateTree 或其他名称。
  • Predicate 只是 Func 的一个特例——它是一个 Func 接受一个对象并返回一个布尔值,这就是为什么你不必在它的声明中写它的原因。 Predicate&lt;Person&gt; 接受Person 类型的参数并返回bool,尽管您声明它Predicate&lt;Person&gt;,而不是Predicate&lt;Person, bool&gt;
  • @Valmont read this (这是关于methog组而不是lambda,但逻辑是一样的;我和Eric Lippert一样好解释^^);有“一个 lambda 没有类型”部分阅读this(在文章中间)。
【解决方案2】:

FindAll 需要 Predicate 而不是 Function,如方法定义 Array.FindAll&lt;T&gt; Method (T[], Predicate&lt;T&gt;) 中所示

当您尝试传递theLambda 时,它正在尝试传递一个函数,而该方法需要一个谓词。您可以尝试将 Lambda 定义为

Predicate<Person> theLambda = (p) => p.Year >= 1992;

谓词是一个返回布尔值的函数,这是 FindAll 方法过滤结果所需要的。

【讨论】:

    【解决方案3】:

    根据here的回答,您可以进行以下操作。

    foreach (Person pers in PersonList.FindAll(new Predicate&lt;Person&gt;(theLambda)))

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-28
      • 1970-01-01
      • 2021-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多