假设我有一个实现IDisposible的结构类型,如果我使用下面的代码:
using (MyStruct ms = new MyStruct()) { InnerAction(ms); //Notice "InnerAction" is "InnerAction(MyStruct ms)" }
当然我看到在使用块之后,ms被处理掉了.然而,“InnerAction”中的结构呢?它是否仍然存在,因为深层复制或它也处置?
如果它仍然存活(未处理),我必须使用“ref”作为“InnerAction”吗?
请给我你的证明:)
大家好.
解决方法
它比你想象的更糟糕:ms甚至没有处理掉.
原因是using语句创建了一个内部副本,它在try / finally构造中调用dispose.
考虑一下这个LinqPad example:
void Main() { MyStruct ms; using (ms = new MyStruct()) { InnerAction(ms); } ms.IsDisposed.Dump(); _naughtyCachedStruct.IsDisposed.Dump(); } MyStruct _naughtyCachedStruct; void InnerAction(MyStruct s) { _naughtyCachedStruct = s; } struct MyStruct : IDisposable { public Boolean IsDisposed { get; set; } public void Dispose() { IsDisposed = true; } }
这是一些反编译的IL:
IL_0000: nop IL_0001: ldloca.s 01 // CS$0$0000 IL_0003: initobj UserQuery.MyStruct IL_0009: ldloc.1 // CS$0$0000 IL_000A: dup IL_000B: stloc.0 // ms IL_000C: dup IL_000D: stloc.0 // ms IL_000E: stloc.2 // CS$3$0001 IL_000F: nop IL_0010: ldarg.0 IL_0011: ldloc.0 // ms
请注意,在IL_000E中,创建了一个编译器生成的本地(CS $3 $0001),并在那里存储了ms的副本.后来…
IL_001B: ldloca.s 02 // CS$3$0001 IL_001D: constrained. UserQuery.MyStruct IL_0023: callvirt System.IDisposable.Dispose IL_0028: nop IL_0029: endfinally
对此本地调用Dispose,而不是ms(存储在位置0中).
结果是ms和InnerAction所持有的副本都没有被处理掉.
结论:不要在using语句中使用结构.
编辑:正如@Weston在评论中指出的那样,you can manually box the struct and act on the boxed instance,因为它然后生活在堆上.这样你就可以获得要处置的实例,但是如果你把它强制转换回using语句中的struct,那么你只会在放置实例之前存储一个副本.此外,拳击移除了远离堆的好处,你可能在这里.
MyStruct ms = new MyStruct(); var disposable = (IDisposable)ms; using (disposable) { InnerAction(disposable); } ((MyStruct)disposable).IsDisposed.Dump();