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

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

谢谢

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

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

Per Eric Lippert :

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

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

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

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

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

几点:

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

不,它没有盒装。

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

 MyClass m = new MyClass() try { // ... } finally { if (m != null) { m.Dispose(); } } 

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

 MyStruct m = new MyStruct() try { // ... } finally { m.Dispose(); } 

由于结构不能为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 } } } 

然后看看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.sm L_0002: initobj ConsoleApplication1.StructBox/MyStruct L_0008: leave.s L_0018 L_000a: ldloca.sm 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 } 

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

行L_0018到L_0026表示m2声明并调用DisposeSomething 。 见L_0021行。

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

在下面写了快速控制台应用程序(注意,我包括了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) {} } }
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 }
.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 }
.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的post,我认为这是正在描述的受约束的虚拟调用。