没有变量/参数声明的C#lambda表达式?

时间:2015-06-17 14:49:00

标签: c# .net linq lambda

当一个将lambda表达式作为参数的方法(例如Enumerable.Where)被调用而没有在表达式中实际声明变量或方法参数时,它调用了什么?

例如,我熟悉这种lambda表达式语法:

public string GetDigits(string input)
{
    return new String(input.Where(i => Char.IsDigit(i)).ToArray());
}

然而,我对find out感到惊讶,这也可以写成:

public string GetDigits(string input)
{
    return new String(input.Where(Char.IsDigit).ToArray());
}

在第二个片段中发生了什么,其中Char.IsDigit()方法(显然)是使用隐式参数调用的?这个语法叫什么?

4 个答案:

答案 0 :(得分:17)

方法不接受 lambdas 作为参数。他们接受委托作为参数。 lambda只是创建委托的一种方式。

另一种方法是提供方法组,如第二个示例中所做的那样,可以将其转换为委托。

类似的方法是使用匿名方法功能。虽然添加了lambdas,但这或多或少会被lambdas取代,所以你不会看到它。使用该语法的示例是:

Func<char, bool> predicate = delegate(char c) { return Char.IsDigit(c); };

另一种方法是使用Delegate.CreateDelegate创建委托。 (这不是你经常看到的东西。)

最后一种方法是拥有一个从其他地方获得的委托变量。 (其他地方会使用其中一个选项创建委托。)

  

在第二个片段中发生了什么,其中Char.IsDigit()方法(显然)是使用隐式参数调用的?这个语法叫什么?

没有被调用。这就是重点。我们正在尝试创建一个委托。委托是一个跟踪要调用的方法的对象,以及一个应该调用它的对象。然后,您可以调用委托,它将调用用于创建委托的方法。所以,你调用IsDigit,你正在创建一个指向IsDigit方法的委托,并且只要该委托就会调用它被调用。

当你使用lambda时,你可能在一个新类中创建一个新方法(它们都没有你可以引用的名字,但它们在运行时会有一个)和正文该匿名方法将调用IsDigit。然后lambda解析为指向该匿名方法的委托,该方法维护另一个示例的语义,该方法在调用时调用匿名方法,该方法在其实现中调用IsDigit。它添加了一个额外的间接层(可能会或可能不会在运行时进行优化)来完成同样的事情。

答案 1 :(得分:10)

Enumerable.Where的签名是:

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate
)

此:

input.Where(i => Char.IsDigit(i))

相当于写作:

Func<char, bool> temp = i => Char.IsDigit(i);
input.Where(temp);

所以它创建了一个匿名函数,其参数i调用Char.IsDigit

此:

input.Where(Char.IsDigit)

相当于

Func<char, bool> temp = Char.IsDigit;
input.Where(temp);

相当于:

Func<char, bool> temp = new Func<char, bool>(Char.IsDigit);
input.Where(temp);

因此它会创建Char.IsDigit的委托,然后将其传递给input.Where

所以第二个移除了#34;中间人&#34; (匿名函数)。在这种特殊情况下,它是合法的&#34;因为匿名函数的i参数被传递&#34;因为&#34;到Char.IsDigit。如果是这样的话会有所不同:

input.Where(i => !Char.IsDigit(i))

在这种情况下,您无法删除中间人(匿名函数)。

所有这些都没有名称(或者你可以调用第一个&#34;创建并将一个委托传递给一个匿名函数&#34;第二个&#34;创建并传递一个从一个方法创建的委托小组&#34; ...但他们不是很漂亮的流行语,他们更能说明你在做什么)

答案 2 :(得分:8)

因为如果找到与预期签名匹配的单个方法,编译器将隐式地将方法组强制转换为委托,在这种情况下,委托将一个char作为输入并且返回bool

答案 3 :(得分:5)

您的Where需要一个Func<char, bool>,它是采用char参数并返回bool的方法的委托。与此委托匹配的任何内容都是此Where的有效参数。

  • 您最初编写的lambda通过类型推断匹配此委托:编译器根据可枚举源的泛型参数期望ichar,并将返回类型推断为{{1因为这就是lambda中的方法调用表达式将返回的内容。
  • bool方法本身也与此匹配。因此,引用该方法是表达同一事物的另一种有效方式。这称为方法组

Char.IsDigit这两个可能的参数的语义等价也是有意义的,如果你考虑对每个lambda表达式,编译器生成一个匿名方法,然后传递该委托所在的匿名方法。

为了说明这一点,请考虑原始代码段:

Where

以上gets lowered by the compiler至:

Where(i => Char.IsDigit(i))

然后:

bool AnAnonymousMethod(char i)
{
    return Char.IsDigit(i);
}

正如你所看到的,lambda语法(在没有捕获变量的情况下,就像这里)只是编写匿名方法的语法糖,然后使用这个新编写的方法的方法组作为参数期待兼容的代表。