Подтвердить что ты не робот

Структуры и IDisposable

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

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 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");
    }
}

UPDATE: Но он работает отлично. Почему?

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");
    }
 }
4b9b3361

Ответ 1

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

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

Ответ 4

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

 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

Ответ 5

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

Теперь...

using (MyType something = new MyType())

является метаэквивалентным для

using (readonly MyType something = new MyType())

с readonly с тем же значением ключевого слова readonly в объявлении class/struct.

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

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

но вы можете

    something.somethingelse = 0;

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

Если MyType - тип значения, модификатор readonly "распространяется на его поля/свойства. Поэтому они не вводили новый тип" constness/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 выполняет "private" копию sss, а sss.n = 12 обращается к "оригиналу" sss, а Dispose обращается к копии.

Ответ 6

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

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

Ответ 7

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

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

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

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

Ответ 8

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

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

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