Если моя структура реализует IDisposable, будет ли он упакован при использовании в операторе using? - PullRequest
19 голосов
/ 10 марта 2010

Если моя структура реализует IDisposable, будет ли он помещен в коробку при использовании в операторе using?

Спасибо

Edit: этот таймдлок является структурой и реализует Idisposable. http://www.interact -sw.co.uk / iangblog / 2004/04/26 / yetmoretimedlocking

Редактировать 2: Глядя на IL, кажется, что если ваша структура предоставляет Dispose () как public, компилятор вызывает Dispose, когда экземпляр структуры выходит из области видимости, если вы забыли вызвать Dispose () (например, вы не используя выражение "using")?

Ответы [ 4 ]

26 голосов
/ 10 марта 2010

За Эрик Липперт :

Вызов IDisposable.Dispose для структуры генерируется как ограниченный виртуальный вызов, который большую часть времени НЕ содержит значение.

Ограниченный виртуальный вызов для типа значения упаковывает значение только в том случае, если виртуальный метод НЕ реализован этим типом. Единственные обстоятельства, при которых виртуальный метод может быть не реализован типом значения, - это когда метод, скажем, ToString и реализован базовым классом System.ValueType.

Подробнее см. В разделе 2.1 раздела III документации CLI.

21 голосов
/ 10 марта 2010

Это дубликат . Когда оператор using блокирует свой аргумент, когда это структура?

ОБНОВЛЕНИЕ: Этот вопрос был темой моего блога в марте 2011 года . Спасибо за отличный вопрос.

Несколько баллов:

  • Как правильно отметили другие, тип значения, который реализует IDisposable, не упаковывается, когда он удаляется в результате контроля, оставляющего оператор using.
  • Это технически нарушение спецификации C #. Спецификация утверждает, что блок finally должен иметь семантику ((IDisposable)resource).Dispose();, что явно является боксерским преобразованием. Мы на самом деле не генерируем конвертацию в бокс. Поскольку в большинстве случаев это именно то, что вам нужно, мы не теряем из-за этого сон.
  • Тип одноразового значения кажется потенциально плохой идеей. Слишком легко случайно сделать копию типа значения; в конце концов, они копируются по значению.
  • С какой стати ты заботишься, будь это коробки или нет? Я надеюсь, что вы не спрашиваете об этом, потому что вы хотите, чтобы метод dispose изменял переменную, содержащую тип значения. Это было бы действительно плохой идеей. Изменчивые типы значений являются злом.
12 голосов
/ 10 марта 2010

Нет, он не упаковывается.

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
        }
    }
}

Тогда посмотрите на ИЛ:

.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.

6 голосов
/ 10 марта 2010

Это не будет коробка (удивил меня).Я думаю, что объяснение 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 - обратите внимание на инструкцию box наСтрока 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, думаю, что это ограниченный виртуальный вызов, который описывает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...