C#里lambda表达式的解糖方式,取决于lambda捕获的东西有多少,有若干不同的方式。
针对题主的例子,假如有这样的代码:
using System; using System.Collections.Concurrent; public class Program { public static void Foo<TKey, TVal>(ConcurrentDictionary<TKey, TVal> concDict, TKey key, TVal newValue) { concDict.AddOrUpdate(key, newValue, (cKey, oldValue) => newValue); } }
C#编译器会在编译时将它解语法糖,生成一些额外的代码。由于这里的lambda捕获了一个方法参数(局部变量的话也一样),这里选择的解糖方式是:
于是解糖后,这个例子的代码会变成下面这样:
(下面的代码里名字都是csc.exe实际生成的样子。它故意要用一些C#不允许用做标识符的符号作为名字的一部分,避免跟用户写的代码里的标识符冲突)
using System; using System.Collections.Concurrent; public class Program { // generated class for captured arguments/local variables private class <>c__DisplayClass1`2'<TKey,TVal> { public TVal newValue; public TVal <Foo>b__0(TKey cKey, TVal oldValue) { return this.newValue; } } public static void Foo<TKey, TVal>(ConcurrentDictionary<TKey, TVal> concDict, TKey key, TVal newValue) { var captures = new <>c__DisplayClass1`2'<TKey,TVal>(); captures.newValue = newValue; var lambda = new Func3<TKey, TVal, TVal>(captures.<Foo>b__0); concDict.AddOrUpdate(key, captures.newValue, lambda); } }
就这样而已。很简单对不对?
数据是动态确定的,代码是静态确定的。可以看到Foo()的参数newValue是动态被捕获到captures里的,但captures的代码是静态生成好的。
顺带放个老传送门:
关于对象与闭包的关系的一个有趣小故事,留意那帖下面的评论区的讨论。
然后这个传送门:
JVM的规范中允许编程语言语义中创建闭包(closure)吗? - RednaxelaFX 的回答 - 知乎