【问题标题】:Difference between lambda expression and method grouplambda表达式和方法组的区别
【发布时间】:2011-07-12 10:10:12
【问题描述】:

有什么区别

Class1.Method1<Guid, BECustomer>("cId", Facade.Customers.GetSingle);

Class1.Method1<Guid, BECustomer>("cId", x => Facade.Customers.GetSingle(x));

?

Resharper 建议使用第一个表达式。

【问题讨论】:

  • 在 C# 中没有语义差异。在 Java 中,类似的事物类型会导致 'this' 的不同绑定!我一直被这个咬住......

标签: c# lambda method-group


【解决方案1】:

结果没有区别。但是,第二个创建了一个额外的重定向:代码将首先调用您的匿名方法,并接受一个名为x 的参数,然后使用该参数调用Facade.Customers.GetSingle。这种重定向根本没有任何好处,这就是 ReSharper 告诉您使用第一个替代方案的原因。

【讨论】:

  • 没错,我认为这只是 lambdas 的语法糖,只有一个参数。
  • @Vladislav:你什么意思?第一个版本不是第二个版本的语法糖。第一个传递GetSingle方法的“函数指针”,而第二个传递匿名方法的“函数指针”。
  • 它不限于单参数 lambda - 方法组适用于任何匹配的委托签名。
  • 在关闭方面有所不同。假设Facade.Customers 随着时间的推移而变化。然后第一个(命名方法中的方法组)将保留原始的Customers,而第二个(来自 lambda 的匿名函数)将反映新的CustomersCustomers 由匿名函数重新评估)。所以在第二个中,评估是“延迟的”。如果这很难理解,请尝试以下代码:string test = "12345"; Func&lt;string, bool&gt; f = test.Contains; Func&lt;string, bool&gt; g = x =&gt; test.Contains(x); test = "changed!"; bool a = f("34"); bool b = g("34");
  • @JeppeStigNielsen:正确。我没有将它包含在这个答案中,因为Facade.Customers 对我来说看起来很静态。我在this answer 中更详细地解释了差异。谢谢。
【解决方案2】:

在幕后,如果您使用 lambda 表达式,编译器会生成更多代码。使用方法组,它只是创建一个指向该方法的新委托:

L_0001: ldstr "cId"
L_0006: ldnull 
L_0007: ldftn void Facade/Customers::GetSingle(valuetype [mscorlib]System.Guid)
L_000d: newobj instance void [mscorlib]System.Action`1<valuetype [mscorlib]System.Guid>::.ctor(object, native int)
L_0012: call void Class1::Method1<valuetype [mscorlib]System.Guid, class BECustomer>(string, class [mscorlib]System.Action`1<!!0>)

使用 lambda 表达式,会在类上创建一个匿名方法(L_0025 上的&lt;Test&gt;b__0),而委托则引用它:

L_0018: ldstr "cId"
L_001d: ldsfld class [mscorlib]System.Action`1<valuetype [mscorlib]System.Guid> Class1::CS$<>9__CachedAnonymousMethodDelegate1
L_0022: brtrue.s L_0037
L_0024: ldnull 
L_0025: ldftn void Class1::<Test>b__0(valuetype [mscorlib]System.Guid)
L_002b: newobj instance void [mscorlib]System.Action`1<valuetype [mscorlib]System.Guid>::.ctor(object, native int)
L_0030: stsfld class [mscorlib]System.Action`1<valuetype [mscorlib]System.Guid> Class1::CS$<>9__CachedAnonymousMethodDelegate1
L_0035: br.s L_0037
L_0037: ldsfld class [mscorlib]System.Action`1<valuetype [mscorlib]System.Guid> Class1::CS$<>9__CachedAnonymousMethodDelegate1
L_003c: call void Class1::Method1<valuetype [mscorlib]System.Guid, class BECustomer>(string, class [mscorlib]System.Action`1<!!0>)

【讨论】:

  • 那么 lambda 和方法组之间是否存在任何实际性能差异?我可以看到另外 5 行 IL,但我们在谈论什么样的现实世界差异?在非常大的数据集上打勾?
  • 最坏的情况是为大集合中的每个项目调用您的Method1 - 每次迭代都会创建一个匿名类的新实例。也就是说,使用 lambda 代替方法组不太可能显着影响您的应用程序。不过,只有测量才能确定。
  • lambda 表达式生成更多代码,但根据this post,lambda 表达式实际上比方法组快一点,并且使用更少的内存,因为“方法组版本每次分配一个新对象它运行,而 lambda 版本使用实例(或静态,根据需要)字段来缓存委托。”
【解决方案3】:

如果您的 Method1&lt;Guid, BECustomer&gt; 接受 Func&lt;Guid, BECustomer&gt; 参数,Func&lt;Guid, BECustomer&gt; 是:

public delegate BECustomer Func(Guid arg);

事实上,所有Func 都是通用委托:

public delegate TResult Func<T, TResult>(T arg);

编译器可以分析您的代码并确定您的Func&lt;Guid, BECustomer&gt;Facade.Customers.GetSingle 的方法组兼容,因为方法签名与委托签名匹配。

这是语法糖,是编译器为您完成繁重工作的另一个示例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-12
    • 1970-01-01
    • 2015-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多