如果我的struct实现了IDisposable,那么当它在using语句中使用时会被装箱吗?
由于
编辑: 这个timedlock是一个结构并实现Idisposable。 http://www.interact-sw.co.uk/iangblog/2004/04/26/yetmoretimedlocking
编辑2:看看IL似乎如果你的结构公开Dispose()为public,当你忘记调用Dispose()时,编译器会在结构的一个实例超出范围时调用Dispose(例如,你是不使用“使用”声明)?
答案 0 :(得分:26)
对结构上的IDisposable.Dispose的调用是作为受约束的虚拟调用生成的,大多数情况下它不会对值进行设置。
如果虚拟方法未由类型实现,则对值类型的受约束虚拟调用仅会对该值进行设置。可以通过值类型实现虚拟方法的唯一情况是,该方法是,例如,ToString,并由基类System.ValueType实现。
有关更多详细信息,请参阅CLI文档的第III部分第2.1节。
答案 1 :(得分:21)
这是When does a using-statement box its argument, when it's a struct?
的副本更新:这个问题是the subject of my blog in March of 2011。谢谢你提出的好问题。
几点:
((IDisposable)resource).Dispose();
的语义,这显然是一个拳击转换。我们实际上并没有生成拳击转换。因为大部分时间这都是你想要的,所以我们不会失去任何睡眠。答案 2 :(得分:12)
不,它没有盒装。
using
不是方法调用。它是语法糖,编译器只是将其转换为:
MyClass m = new MyClass()
try
{
// ...
}
finally
{
if (m != null) {
m.Dispose();
}
}
它从不在声明中使用IDisposable
,也从不将实例传递给其他任何东西。对于结构体,编译器实际上生成了更小的东西:
MyStruct m = new MyStruct()
try
{
// ...
}
finally
{
m.Dispose();
}
由于结构不能为空。
现在,要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.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
}
行L_0000到L_0017表示m
声明和using
。没有拳击。
行L_0018到L_0026表示m2
声明并呼叫DisposeSomething
。见L_0021 box
。
答案 3 :(得分:6)
这不会包装盒子(让我感到惊讶)。我认为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的帖子,我认为这是正在描述的受约束的虚拟调用。