Если моя структура реализует IDisposable, она будет помещена в бокс при использовании в операторе using?
Если моя структура реализует IDisposable, она будет помещена в поле при использовании в операторе using?
Спасибо
Изменить:
этот timedlock является структурой и реализует Idisposable.
http://www.interact-sw.co.uk/iangblog/2004/04/26/yetmoretimedlocking
Отредактируйте 2: Посмотрите на ИЛ. Кажется, что если ваша структура раскрывает Dispose() как общедоступную, компилятор вызывает Dispose, когда экземпляр структуры выходит из области видимости, если вы забыли вызвать Dispose() (например, вы не используя инструкцию "using" )? Sooo кажется, что большинство из вас, парни, ошибаются!
Ответы
Ответ 1
Per Эрик Липперт:
Вызов IDisposable.Dispose на структуре генерируется как ограниченный виртуальный вызов, который большую часть времени НЕ вставляет значение.
Ограниченный виртуальный вызов по типу значения вводит только значение, если виртуальный метод НЕ реализуется типом. Единственными обстоятельствами, при которых виртуальный метод может быть не реализован типом значения, является тот метод, который, скажем, ToString, и реализуется базовым классом System.ValueType.
Подробнее см. раздел 2.1 раздела III документации CLI.
Ответ 2
Это дубликат Когда используется оператор-оператор в его аргументе, когда это структура?
UPDATE: этот вопрос был тема моего блога в марте 2011 года. Спасибо за большой вопрос.
Несколько точек:
- Как правильно указали другие, тип значения, который реализует IDisposable, не помещается в бокс, когда он удаляется из-за того, что элемент управления выходит за оператор using.
- Это технически является нарушением спецификации С#. Спецификация заявляет, что блок finally должен иметь семантику
((IDisposable)resource).Dispose();
, которая явно является бокс-конверсией. Мы фактически не генерируем конверсию бокса. Поскольку большую часть времени это то, что вы хотите, мы не теряем сна.
- Тип одноразового значения кажется потенциально плохим. Это слишком просто, чтобы случайно сделать копию типа значения; они копируются по значению в конце концов.
- Почему вам все равно, нужны ли эти коробки или нет? Надеюсь, вы не спросите об этом, потому что хотите, чтобы метод dispose мутировал переменную, содержащую тип значения. На самом деле это была бы плохая идея. Типы допустимых значений - зло.
Ответ 3
Нет, он не попадает в коробку.
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
. См. On line L_0021 box
.
Ответ 4
Это не будет в коробке (удивил меня). Я думаю, что объяснение 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 для BoxTest - обратите внимание на инструкцию по ящику на линии L_000a (астр. акцент мой)
.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 (где мы используем инструкцию using с нашей конструкцией IDisposable):
.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, я думаю, что это ограниченный виртуальный вызов, который описывает.