当一个将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()方法(显然)是使用隐式参数调用的?这个语法叫什么?
答案 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
的有效参数。
i
为char
,并将返回类型推断为{{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语法(在没有捕获变量的情况下,就像这里)只是编写匿名方法的语法糖,然后使用这个新编写的方法的方法组作为参数期待兼容的代表。