Структура и IDisposable - PullRequest
       10

Структура и IDisposable

13 голосов
/ 27 октября 2011

Интересно, почему он не компилируется?

public static void Main(string[] args)
{
    using (MyStruct sss = new MyStruct())
    {

        sss.s = "fsdfd";// Cannot modify members of 'sss' because it is a 'using variable' 

        //sss.Set(12);    //but it's ok
    }
}

public struct MyStruct : IDisposable
{
    public int n;
    public string s;

    public void Set(int n)
    {
        this.n = n;
    }
    public void Dispose()
    {
        Console.WriteLine("dispose");
    }
}

ОБНОВЛЕНИЕ : Но работает идеально.Почему?

public static void Main(string[] args)
        {

            using (MyClass sss = new MyClass())
            {
                sss.Field = "fsdfd"; 
            }


        }

public class MyClass:IDisposable {

    public string Property1 { get; set; }
    public string Field;
    public void Method1 (){}

    public void Dispose()
    {
        Console.WriteLine("dispose class");
    }
 }

Ответы [ 8 ]

19 голосов
/ 27 октября 2011

Несколько человек связались с моей статьей о мутирующих типах значений и о том, почему это плохая идея.Хотя важно понимать эти концепции, когда понимаешь, почему плохая идея распоряжаться структурой, а хуже - изменять структуру, когда ты это делаешь, на самом деле это не та статья, на которую нужно ссылаться.Тот, который вы хотите прочитать, объясняющий все это в мучительных подробностях:

http://ericlippert.com/2011/03/14/to-box-or-not-to-box/

Короче говоря: «использование» создает копию типа значения,и поэтому вы располагаете копией .Это означает, что вы должны быть очень осторожны - если значение, скажем, дескриптор ОС, может быть много копий этого значения, лежащих вокруг памяти, и вам нужно убедиться, что вы утилизируете его точно один раз, независимо от того,сколько существует копий.

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

4 голосов
/ 27 октября 2011

Идея оператора using состоит в том, чтобы обеспечить удаление ресурса при выходе из блока.

Когда вы назначаете структурную переменную, вы фактически заменяете ее совершенно новым объектом этого типа. Так что в этом контексте вы бы избавились от объекта, от которого якобы избавились бы.

3 голосов
/ 27 октября 2011

Сценарии класса и структуры на самом деле одинаковы, но вы видите разные эффекты.

Когда вы изменяете пример класса на:

using (MyClass sss = new MyClass())
{
    sss = null;          // the same error
    sss.Field = "fsdfd"; // ok
}

Вы получите ту же ошибку при первом назначении.

Объяснение: Вы не можете изменить (изменить) переменную using. Но для класса, который применяется к ссылке, а не к экземпляру.

И урок: не используйте структуры. И особенно не используйте изменяемые структуры.

2 голосов
/ 27 октября 2011

Наконец-то я это понял :-) Я опубликую свое мнение :-): -)

Сейчас ...

using (MyType something = new MyType())

метаэквивалентно

using (readonly MyType something = new MyType())

с readonly, имеющим то же значение ключевого слова readonly в объявлении класса / структуры.

Если MyType является ссылкой, то это ссылка (а не ссылка)объект), который "защищен".Таким образом, вы не можете сделать:

using (readonly MyType something = new MyType())
    something = null;

, но вы можете

    something.somethingelse = 0;

в используемом блоке.

Если MyType является типом значения, то readonly «Модификатор» распространяется на его поля / свойства.Поэтому они не ввели новый тип «const-ness / readonly-ness» в using, они просто использовали тот, который у них был.

Таким образом, вопрос должен быть: почему я не могу изменитьполя / свойства readonly типов значений?

Обратите внимание, что если вы сделаете это:

public void Dispose()
{
    Console.WriteLine("During dispose: {0}", n);
}

var sss = new MyStruct();

using (sss)
{
    sss.n = 12;
    Console.WriteLine("In using: {0}", sss.n); // 12
}

Console.WriteLine("Outside using: {0}", sss.n); // 12

Результат

In using: 12
During dispose: 0
Outside using: 12

, поэтому using делает«Частная» копия sss, и sss.n = 12 обращается к «оригиналу» sss, в то время как Dispose обращается к копии.

2 голосов
/ 27 октября 2011

Рассмотрим следующее:

 interface IFoo: IDisposable { int Bar {get;set;}}

 struct Foo : IFoo
 {
   public int Bar { get; set; }
   public void Dispose() 
   {
     Console.WriteLine("Disposed: {0}", Bar);
   }
 }

Теперь сделайте:

  IFoo f = new Foo();

  using (f)
  {
    f.Bar = 42;
  }

  Console.WriteLine(f.Bar); 

Это печатает:

Disposed: 42
42
1 голос
/ 27 октября 2011

Я не уверен на 100%, поэтому, пожалуйста, поправьте меня, если я ошибаюсь.

Причина, по которой компилятор позволяет вам изменять поля класса в этом сценарии, но не структуру, связана с управлением памятью. В случае класса (то есть ссылочного типа), не объект, а сама ссылка является значением . Поэтому, когда вы изменяете поле в этом объекте, вы манипулируете не значением, а блоком памяти в другом месте, на который ссылается значение. В случае структуры объектом является значение , поэтому, когда вы манипулируете полем в структуре, вы фактически манипулируете значением, которое в данный момент считается доступным только для чтения.

Причина, по которой компилятор разрешает вызов метода (который, в свою очередь, изменяет поле), заключается просто в том, что он не может анализировать достаточно глубоко, чтобы определить, выполняет ли метод такую ​​модификацию или нет.

В MS Connect существует (закрытый) случай, который может пролить больше света: Невозможно присвоить полю структуры с IDisposable, а когда это переменная использования - ошибка CS1654

1 голос
/ 27 октября 2011

В блоке using объект доступен только для чтения и не может быть изменен или переназначен.

проверьте эту ссылку: http://msdn.microsoft.com/en-us/library/yh598w02.aspx

0 голосов
/ 27 октября 2011

Структура помечена как доступная только для чтения. Вы пытаетесь изменить общедоступную переменную-член, которую компилятор помечает как доступную только для чтения и поэтому запрещает.

Однако, вызов Set() разрешен, потому что компилятор не может знать, что вызов изменит состояние структуры. По сути, это хитрый способ изменить значение только для чтения!

Взгляните на сообщение Эрика Липперта о мутирующем readonly структурах для получения дополнительной информации.

...