No, it does not get boxed.
using
is not a method call. It's syntactic sugar that the compiler just converts into, roughtly, this:
MyClass m = new MyClass()
try
{
// ...
}
finally
{
if (m != null) {
m.Dispose();
}
}
It never uses IDisposable
in the declaration and never passes the instance to anything else. For a struct, the compiler actually generates something even smaller:
MyStruct m = new MyStruct()
try
{
// ...
}
finally
{
m.Dispose();
}
Since a struct can't be null.
Now, to be 100% sure it never boxes, look at the IL.
Try this sample code:
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
}
}
}
Then look at the 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
}
Lines L_0000 through L_0017 represent the m
declaration and using
. There is no boxing.
Lines L_0018 through L_0026 represent the m2
declaration and call to DisposeSomething
. See on line L_0021 box
.