为啥 C# 8.0 中使用声明的变量的闭包不同?
Posted
技术标签:
【中文标题】为啥 C# 8.0 中使用声明的变量的闭包不同?【英文标题】:Why are closures different for variables from C# 8.0 using declarations?为什么 C# 8.0 中使用声明的变量的闭包不同? 【发布时间】:2019-12-26 19:47:26 【问题描述】:我注意到 C# 8.0 编译器为使用 C# 8.0 using 声明声明的捕获的 IDisposable
变量构建闭包类的方式与使用经典 using 语句声明的变量不同。
考虑这个简单的类:
public class DisposableClass : IDisposable
public void Dispose()
还有这个示例代码:
public void Test()
using var disposable1 = new DisposableClass();
using var disposable2 = new DisposableClass();
Action action = () => Console.Write($"disposable1disposable2");
编译器生成以下代码:
[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
public DisposableClass disposable1;
public DisposableClass disposable2;
internal void <Test>b__0()
Console.Write(string.Format("01", disposable1, disposable2));
public void Test()
<>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
<>c__DisplayClass0_.disposable1 = new DisposableClass();
try
<>c__DisplayClass0_.disposable2 = new DisposableClass();
try
Action action = new Action(<>c__DisplayClass0_.<Test>b__0);
finally
if (<>c__DisplayClass0_.disposable2 != null)
((IDisposable)<>c__DisplayClass0_.disposable2).Dispose();
finally
if (<>c__DisplayClass0_.disposable1 != null)
((IDisposable)<>c__DisplayClass0_.disposable1).Dispose();
这看起来完全没问题。但后来我注意到,如果我用 using 语句声明这两个变量,则生成的闭包类会完全不同。这是示例代码:
public void Test()
using (var disposable1 = new DisposableClass())
using (var disposable2 = new DisposableClass())
Action action = () => Console.Write($"disposable1disposable2");
这就是我得到的:
[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
public DisposableClass disposable1;
[CompilerGenerated]
private sealed class <>c__DisplayClass0_1
public DisposableClass disposable2;
public <>c__DisplayClass0_0 CS$<>8__locals1;
internal void <Test>b__0()
Console.Write(string.Format("01", CS$<>8__locals1.disposable1, disposable2));
为什么会这样?其余的代码看起来是一样的,我认为 using 声明应该与一个 using 语句完全相同,该语句被认为是块它在其中声明的当前块。
更不用说使用声明生成闭包类的方式看起来更清晰,最重要的是,更更容易通过反射进行探索。
如果有人知道为什么会发生这种情况,我希望得到一些见解。
谢谢!
【问题讨论】:
【参考方案1】:编译器正在为每个“作用域”生成一个[CompilerGenerated]
class
...在第一个示例中,有一个作用域,即整个Test()
方法。在第二个示例中(您没有给出),有两个范围,两个 using
。
第二个例子的代码大概是:
public void Test()
using (var disposable1 = new DisposableClass())
using (var disposable2 = new DisposableClass())
Action action = () => Console.Write($"disposable1disposable2");
正如 juharr 所指出的,这两个代码块产生相同的代码:
using (DisposableClass disposable1 = new DisposableClass(), disposable2 = new DisposableClass())
Action action = () => Console.Write($"disposable1disposable2");
和
using var disposable1 = new DisposableClass();
using var disposable2 = new DisposableClass();
Action action = () => Console.Write($"disposable1disposable2");
【讨论】:
所以更好的比较是using(DisposableClass disposable1 = new DisposableClass(), disposable2 = new DisposableClass())
@juharr 是的,从我用sharplab 做的一个小测试来看,就像你写的:sharplab.io/… 和
还有sharplab.io/…
这很有道理,谢谢!此外,为了更清楚起见,我已经使用第二个示例中使用的代码更新了我的问题。我使用了两个堆叠的 using
语句,然后在第二个下方使用了一个块。以上是关于为啥 C# 8.0 中使用声明的变量的闭包不同?的主要内容,如果未能解决你的问题,请参考以下文章