当使用一个非常简单的表达式作为创建ILookup的键时,Enumerable.ToLookup<TSource,?TKey> Method (IEnumerable<TSource>,?Func<TSource,?TKey>)我可以使用lambda表达式:
var lk = myItems.ToLookup((x) => x.Name);
Run Code Online (Sandbox Code Playgroud)
或本地功能:
var lk = myItems.ToLookup(ByName);
string ByName(MyClass x)
{
return x.Name;
}
Run Code Online (Sandbox Code Playgroud)
我很好奇这个简单案例是否存在差异.
在他对Local函数vs Lambda C#7.0的回答中,SO用户svick给出了一个很好的论据 - 为什么-in general-局部函数比lambdas更好.
重要的一点是性能的差异:
创建lambda时,必须创建一个委托,在这种情况下这是一个不必要的分配.本地函数实际上只是函数,不需要代理.
但是因为ToLookup()无论如何我们都将它传递给了一个委托.性能还有差异吗?
我可以想象编译器必须为myItems.ToLookup的每次调用创建一个新的委托lambda,而本地方法只需要一个委托实例; 这是真的?
此外,本地函数在捕获局部变量时更有效:lambdas通常将变量捕获到类中,而本地函数可以使用struct(使用ref传递),这又避免了分配.
然而,因为表达不从外部范围使用变量,也不必是封闭的陈述由里德·科普塞和扩大通过埃里克利珀在回答在C#闭包是Lambda表达式?:
lambda可以使用闭包来实现,但它本身不一定是闭包.- Reed Copsey
[...]
一个可以被视为对象的函数只是一个委托.使lambda成为闭包的原因是它捕获了它的外部变量.- Eric Lippert
这多少有些矛盾埃里克利珀自己是他的回答到指定的本地函数来代表 埃里克利珀解释一个局部函数为命名的λ:
本地函数基本上只是一个带有关联名称的lambda.
但这是一个较小的技术细节和lambda/local函数的代表,它们捕获外部范围变量.
这个简单的表达式不是递归的,不是泛型的,也不是迭代器.看起来更好的是一个意见问题.
那么,简单的非捕获,非递归,非泛型和非迭代器lambda表达式和本地函数之间的性能(或其他)是否有任何差异?
使用当前版本的编译器(Roslyn 2.8.0),具有lambda的版本更有效,因为它缓存了委托.
查看具有两个样本的代码的IL在不同的方法中,它是有效的:
sealed class HelperClass
{
public static readonly HelperClass Instance = new HelperClass();
public static Func<MyClass, string> CachedDelegate;
internal string LambdaByName(MyClass x)
{
return x.Name;
}
internal string LocalFunctionByName(MyClass x)
{
return x.Name;
}
}
void Lambda(IEnumerable<MyClass> myItems)
{
var lk = myItems.ToLookup(HelperClass.CachedDelegate ??
(HelperClass.CachedDelegate =
new Func<MyClass, string>(HelperClass.Instance.LambdaByName)));
}
void LocalFunction(IEnumerable<MyClass> myItems)
{
var lk = myItems.ToLookup(
new Func<MyClass, string>(HelperClass.Instance.LocalFunctionByName)));
}
Run Code Online (Sandbox Code Playgroud)
请注意如何Lambda仅分配委托一次并在之后使用缓存委托,同时LocalFunction每次分配委托.这Lambda在这种特定情况下更有效.
虽然有一个关于GitHub的提议,要改变编译器的LocalFunction效率Lambda.
| 归档时间: |
|
| 查看次数: |
336 次 |
| 最近记录: |