Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Struct and IDisposable

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");
    }
 }
like image 791
Alexandre Avatar asked Oct 27 '11 10:10

Alexandre


People also ask

What is IDisposable used for?

IDisposable Interface (System) Provides a mechanism for releasing unmanaged resources.

What is System IDisposable?

IDisposable is an interface defined in the System namespace. It is used to release managed and unmanaged resources. Implementing IDisposable interface compels us to implement 2 methods and 1 boolean variable – Public Dispose() : This method will be called by the consumer of the object when resources are to be released.

Is IDisposable called automatically?

By default, the garbage collector automatically calls an object's finalizer before reclaiming its memory. However, if the Dispose method has been called, it is typically unnecessary for the garbage collector to call the disposed object's finalizer.

What is a ref struct?

What is a ref struct? Well, a ref struct is basically a struct that can only live on the stack. Now a common misconception is that since classes are reference types, those live on the heap and structs are value types and those live on the stack.


2 Answers

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.

like image 39
Henk Holterman Avatar answered Oct 12 '22 11:10

Henk Holterman


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?

like image 176
Eric Lippert Avatar answered Oct 12 '22 12:10

Eric Lippert