如果我的struct实现了IDisposable,那么当在using语句中使用时它会被装箱吗?

joh*_*een 23 c#

如果我的struct实现了IDisposable,那么当在using语句中使用时它会被装箱吗?

谢谢

编辑:这个timedlock是一个结构并实现Idisposable. http://www.interact-sw.co.uk/iangblog/2004/04/26/yetmoretimedlocking

编辑2:看看IL似乎如果你的结构公开Dispose()为public,当你忘记调用Dispose()时,编译器会在结构的一个实例超出范围时调用Dispose(例如,你没有使用"使用"声明)?

kd7*_*kd7 28

Per Eric Lippert:

对结构上的IDisposable.Dispose的调用生成为受约束的虚拟调用,大多数情况下不会对该值进行封装.

如果虚拟方法未由类型实现,则对值类型的受约束虚拟调用仅会对值进行设置.可以通过值类型实现虚拟方法的唯一情况是,该方法是ToString,并由基类System.ValueType实现.

有关更多详细信息,请参阅CLI文档的第III部分2.1.

  • +1我刚刚创建了一个测试应用程序,看看会发生什么,并在ildasm中查看它.没有拳击正在进行.正如这个答案所表明的那样. (5认同)

Eri*_*ert 23

这是一个复制的什么时候一个using语句框的参数,当它是一个结构?

更新:这个问题是我2011年3月的博客主题.谢谢你提出的好问题.

几点:

  • 正如其他人已正确指出的那样,实现IDisposable的值类型在由于控件离开using语句而被处置时不会被加框.
  • 这在技术上违反了C#规范.规范声明finally块的语义应该 ((IDisposable)resource).Dispose(); 是拳击转换.我们实际上并没有生成拳击转换.因为大部分时间这都是你想要的,我们不会失去任何睡眠.
  • 一次性价值类型似乎是一个潜在的坏主意.很容易意外地制作一个值类型的副本; 毕竟它们是按价值复制的.
  • 为什么你在乎这个盒子呢?我希望你不要问这个,因为你想让dispose方法改变包含值类型的变量.那确实是一个坏主意.可变值类型是邪恶的.

  • 您不希望将其装箱的最明显原因是出于性能原因(例如,您希望在C#中模拟C++ RAII,这是低成本的). (10认同)
  • 我正在应用 `using(new SomeScopedBehavior())` 习惯用法来保证我的 End 方法被每个 Begin 方法调用,即使我有多个返回或异常。我也在使用 Unity,所以我试图避免每秒为这个调用创建 3+kb 的堆垃圾。你会争辩说我应该做 `try { ... } finally { ... }` 来代替清楚起见,还是你会说 `using` 成语和结构体在这里做的更好?我的`IDisposable` 对象只是包装了静态方法,所以它不是我的鼻子。 (5认同)

Sam*_*eff 12

不,它没有盒装.

using不是方法调用.它是语法糖,编译器只是将其转换为:

MyClass m = new MyClass()
try
{
    // ...
}
finally
{
    if (m != null) {
        m.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

它从不IDisposable在声明中使用,也从不将实例传递给其他任何东西.对于结构体,编译器实际上生成了更小的东西:

MyStruct m = new MyStruct()
try
{
    // ...
}
finally
{
    m.Dispose();
}
Run Code Online (Sandbox Code Playgroud)

由于结构不能为null.

现在,要100%确定它从不装箱,看看IL.

试试这个示例代码:

class StructBox
{
    public static void Test()
    {
        using(MyStruct m = new MyStruct())
        {

        }


        MyStruct m2 = new MyStruct();
        DisposeSomething(m2);
    }

    public static void DisposeSomething(IDisposable disposable)
    {
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }

    private struct MyStruct : IDisposable
    {           
        public void Dispose()
        {
            // just kidding
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后看看IL:

.method public hidebysig static void Test() cil managed
{
    .maxstack 1
    .locals init (
        [0] valuetype ConsoleApplication1.StructBox/MyStruct m,
        [1] valuetype ConsoleApplication1.StructBox/MyStruct m2)
    L_0000: ldloca.s m
    L_0002: initobj ConsoleApplication1.StructBox/MyStruct
    L_0008: leave.s L_0018
    L_000a: ldloca.s m
    L_000c: constrained ConsoleApplication1.StructBox/MyStruct
    L_0012: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0017: endfinally 
    L_0018: ldloca.s m2
    L_001a: initobj ConsoleApplication1.StructBox/MyStruct
    L_0020: ldloc.1 
    L_0021: box ConsoleApplication1.StructBox/MyStruct
    L_0026: call void ConsoleApplication1.StructBox::DisposeSomething(class [mscorlib]System.IDisposable)
    L_002b: ret 
    .try L_0008 to L_000a finally handler L_000a to L_0018
}
Run Code Online (Sandbox Code Playgroud)

行L_0000到L_0017表示m声明和using.没有拳击.

行L_0018到L_0026表示m2声明和调用DisposeSomething.见L_0021 box.


JMa*_*sch 8

这不会打包(让我感到惊讶).我认为bnkdev的解释涵盖了它.这是我如何证明它:

在下面写了快速控制台应用程序(注意,我包括了BoxTest(),我知道它会打包,所以我有东西可以比较).

然后我使用Reflector将编译后的输出反汇编为IL(你可以使用ILDASM).


namespace StructInterfaceBoxingTest
{
    public struct TestStruct : IDisposable
    {
        #region IDisposable Members

        public void Dispose()
        {
            System.Console.WriteLine("Boo!");
        }

        #endregion
    }


    class Program
    {
        static void Main(string[] args)
        {
            using (TestStruct str = new TestStruct())
            {

            }
        }

        static void BoxTest()
        {
            TestStruct str = new TestStruct();
            ThisWillBox(str);
        }

        static void ThisWillBox(object item) {}
    }
}

好的,首先,这是IL for BoxTest - 注意L_000a线上的方框说明(astersik强调我的)


.method private hidebysig static void BoxTest() cil managed
{
    .maxstack 1
    .locals init (
        [0] valuetype StructInterfaceBoxingTest.TestStruct str)
    L_0000: nop 
    L_0001: ldloca.s str
    L_0003: initobj StructInterfaceBoxingTest.TestStruct
    L_0009: ldloc.0 
    L_000a: **box** StructInterfaceBoxingTest.TestStruct
    L_000f: call void StructInterfaceBoxingTest.Program::ThisWillBox(object)
    L_0014: nop 
    L_0015: ret 
}

现在看看Main(我们使用带有IDisposable结构的using语句):


.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 1
    .locals init (
        [0] valuetype StructInterfaceBoxingTest.TestStruct str)
    L_0000: nop 
    L_0001: ldloca.s str
    L_0003: initobj StructInterfaceBoxingTest.TestStruct
    L_0009: nop 
    L_000a: nop 
    L_000b: leave.s L_001c
    L_000d: ldloca.s str
    L_000f: constrained StructInterfaceBoxingTest.TestStruct
    L_0015: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_001a: nop 
    L_001b: endfinally 
    L_001c: nop 
    L_001d: ret 
    .try L_0009 to L_000d finally handler L_000d to L_001c
}

请注意L_000f行上的约束关键字.我找不到该关键字究竟是什么意思的参考,但是如果你读过bnkdev的帖子,我认为这是正在描述的受约束的虚拟调用.