Struct and IDisposable

左心房为你撑大大i 提交于 2019-12-03 11:40:21

问题


I wonder why does it not compile?

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

UPDATE: But it works perfect. Why?

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

回答1:


The class and struct scenarios are actually the same but you see different effects.

When you change the class example to :

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

You will get the same error on the first assignment.

The explanation is: You cannot change (mutate) the using-variable. But for a class that applies to the reference, not to the instance.

And the lesson is: Don't use structs. And especially don't use mutable structs.




回答2:


A number of people have linked to my article about mutating value types and why it is a bad idea. Though it is important to understand those concepts when understanding why its a bad idea to dispose a struct, and a worse idea to mutate the struct when you do, that is not actually the right article to link to. The one you want to read that explains all this in excruciating detail is:

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

In short: "using" makes a copy of the value type, and you are therefore disposing a copy. That means you have to be very careful -- if the value is, say, an OS handle, there might be lots of copies of that value lying around memory, and you'll need to make sure that you dispose it exactly once no matter how many copies there are.

See also If my struct implements IDisposable will it be boxed when used in a using statement?




回答3:


The idea of a using statement is to ensure that a resource is disposed of when you leave the block.

When you assign to a struct variable, you are effectively replacing it with a completely new object of that type. So in this context you would have got rid of the object that was supposedly going to be disposed of.




回答4:


Consider the following:

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

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

Now do:

  IFoo f = new Foo();

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

  Console.WriteLine(f.Bar); 

This prints:

Disposed: 42
42



回答5:


Finally I have comprehended it :-) I'll post my view :-) :-)

Now...

using (MyType something = new MyType())

is meta-equivalent to

using (readonly MyType something = new MyType())

with readonly having the same meaning of the readonly keyword in class/struct declaration.

If MyType is a reference, then it is the reference (and not the referenced object) that is "protected". So you can't do:

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

but you can

    something.somethingelse = 0;

in the using block.

If MyType is a value type, the readonly "modifier" extends to its fields/properties. So they didn't introduced a new type of "const-ness/readonly-ness" in using, they simply used the one they had.

So the question should be: why can't I modify fields/properties of readonly value types?

Note that if you do this:

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

Result

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

so the using is doing a "private" copy of sss, and sss.n = 12 is accessing the "original" sss, while Dispose is accessing the copy.




回答6:


Within the using block, the object is read-only and cannot be modified or reassigned.

check this link: http://msdn.microsoft.com/en-us/library/yh598w02.aspx




回答7:


I am not 100% sure on this, so anyone please correct me if I'm wrong.

The reason that the compiler allows you to modify fields of a class in this scenario, but not a struct, is related to memory management. In the case of a class (i.e a reference type), the not the object, but the reference itself is the value. So when you modify a field in that object, you are not manipulating the value, but a memory block somewhere else, referenced by the value. In the the case of a struct, the object is the value, so when you manipulate a field in the struct, you are in fact manipulating the value that is considered read-only at this time.

The reason the compiler will allow the method call (that in turn modifies a field) is simply that it cannot analyze deeply enough to determine whether the method performs such modification or not.

There is a (closed) case at MS Connect regarding this, that may shed some more light: Cannot assign to field of struct with IDisposable and when it is a using variable - CS1654 error




回答8:


The struct is flagged as read-only. You're trying to modify a public member variable, which the compiler flags as read-only and so prohibits.

However, the call to Set() is allowed because the compiler has no way of knowing the the call will mutate the state of the struct. It is, in effect, a crafty way of mutating a readonly value!

Take a look at Eric Lippert's post of mutating readonly structs for more info.



来源:https://stackoverflow.com/questions/7914423/struct-and-idisposable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!