[英]Performance of assigning a simple lambda expression or a local function to a delegate
当使用非常简单的表达式作为键来创建带有Enumerable.ToLookup<TSource, TKey> Method (IEnumerable<TSource>, Func<TSource, TKey>)
的ILookup时,我可以使用lambda表达式:
var lk = myItems.ToLookup((x) => x.Name);
或局部函数:
var lk = myItems.ToLookup(ByName);
string ByName(MyClass x)
{
return x.Name;
}
我很好奇在这种简单情况下是否有所不同。
在他的回答到本地功能VS LAMBDA C#7.0 SO用户svick给出了一个很好的理由,为什么-in中普通的本地函数是最好lambda表达式。
重要的一点是性能上的差异:
创建lambda时,必须创建一个委托,在这种情况下,这是不必要的分配。 本地函数实际上只是函数,不需要委托。
但是,由于我们将其传递给ToLookup()
所以无论如何都会创建一个委托。 性能上还是有区别的吗?
我可以想象,编译器必须为myItems.ToLookup的每次调用创建一个新的委托lambda,而本地方法只需要一个委托实例即可。 这是真的?
svick 回答中性能的第二点差异是变量的捕获和闭包的创建:
同样,局部函数在捕获局部变量时效率更高:lambda通常将变量捕获到类中,而局部函数可以使用结构(使用ref传递),这又避免了分配。
但是,由于表达式不使用外部作用域中的变量,因此不必像Reed Copsey 所说的那样使用闭包,也不需要Eric Lippert 扩展 闭包来回答C#闭包中的Lambda表达式吗? :
Lambda可以使用闭包来实现,但它本身不一定是闭包。 — 里德·科普西
[...]
可以视为对象的函数只是一个委托。 使lambda成为闭包的原因是它捕获了其外部变量。 — 埃里克·利珀特
这多少有些矛盾埃里克利珀自己是他的回答到指定的本地函数来代表 埃里克利珀解释一个局部函数为命名的λ:
本地函数基本上只是具有关联名称的lambda。
但这只是在较少的技术细节上,对于确实捕获外部作用域变量的lambda /局部函数的委托。
这个简单的表达式不是递归的,不是通用的,也不是迭代器。 看起来更好的是意见问题。
那么,简单的不捕获,非递归,非泛型和非迭代器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)));
}
请注意, Lambda
如何只分配一次委托,然后再使用缓存的委托,而LocalFunction
每次都会分配委托。 这使Lambda
在这种特定情况下更加有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.