结构类型的深层副本是否也在"使用......"块中处理?

xqM*_*vKW 6 c# struct idisposable using

假设我有一个实现IDisposible的结构类型,如果我使用下面的代码:

using (MyStruct ms = new MyStruct())
{
     InnerAction(ms);   //Notice "InnerAction" is "InnerAction(MyStruct ms)"
}
Run Code Online (Sandbox Code Playgroud)

当然我看到在使用块之后,ms被处理掉了.然而,"InnerAction"中的结构呢?它是否仍然存在,因为深层复制或它也处置?

如果它仍然存活(未处理),我必须使用"ref"作为"InnerAction"吗?

请给我你的证明:)

大家好.

cod*_*zen 4

这比你想象的更糟糕:ms甚至没有被处置。

原因是该using语句创建了一个内部副本,并在 try/finally 构造中调用了 dispose。

考虑这个LinqPad 示例

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是一些反编译的 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
Run Code Online (Sandbox Code Playgroud)

请注意,在 IL_000E 中,创建了编译器生成的 local ( ) 并在那里存储了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  
Run Code Online (Sandbox Code Playgroud)

Dispose针对此本地调用,而不是ms(存储在位置 0 中)。

结果是两者ms和保留的副本InnerAction都没有被处理。

结论:不要在using语句中使用结构。

编辑:正如@Weston在评论中指出的那样,您可以手动装箱结构并对装箱的实例进行操作,因为它随后位于堆上。通过这种方式,您可以释放实例,但如果您将其强制转换回语句中的结构using,则最终只会在释放实例之前存储一个副本。此外,拳击消除了远离堆的好处,而您可能已经做到了这一点。

MyStruct ms = new MyStruct();
var disposable = (IDisposable)ms;
using (disposable)
{
    InnerAction(disposable);
}

((MyStruct)disposable).IsDisposed.Dump();
Run Code Online (Sandbox Code Playgroud)