1

In C#, if I have the following struct:

internal struct myStruct : IDisposable
{
    public int x;

    public void Dispose()
    {
        x = 0;
    }
}

then do this in Main:

using (myStruct myStruct = new myStruct())
{
   myStruct.x = 5;
}

it fails saying that myStruct is readonly. That makes sense as myStruct is a value-type.

Now if I add the folling function to the struct:

public void myfunc(int x)
{
    this.x = x;
}

and change the Main code to this:

using (myStruct myStruct = new myStruct())
{
    myStruct.myfunc(5);
    Console.WriteLine(myStruct.x);
}

it works. Why ?

tomatoRadar
  • 401
  • 3
  • 11
  • 1
    http://stackoverflow.com/questions/6001280/why-is-a-variable-declared-in-a-using-statement-treated-as-readonly – Millie Smith Mar 05 '15 at 03:22
  • @MillieSmith I saw that earlier before posting. My query is why does a workaround work. – tomatoRadar Mar 05 '15 at 03:36
  • I don't know. I just posted it because it was relevant and figured it would help. – Millie Smith Mar 05 '15 at 03:38
  • A value-type *is* its value. C# is attempting to prevent you from changing the thing you're `using` out from under it. It's like `using (var a = new MyClass()) { a = newMyClass(); }` - which is also forbidden. The reason the workaround works is that the compiler can only go so far in preventing you from messing with it, I suppose. – Blorgbeard Mar 05 '15 at 03:49

1 Answers1

4

The short answer is "because the C# specification says so". Which, I admit, may be a bit unsatisfying. But that's how it is.

The motivation is, I'm sure, as commenter Blogbeard suggests: while it's practical to enforce read-only on the field access, it's not practical to do so from within a type. After all, the type itself has no way to know how a variable containing a value of that type was declared.

The key part of the C# specification (from the v5.0 spec) is here, on page 258 (in the section on the using statement):

Local variables declared in a resource-acquisition are read-only, and must include an initializer. A compile-time error occurs if the embedded statement attempts to modify these local variables (via assignment or the ++ and operators), take the address of them, or pass them as ref or out parameters.

Since in the case of a value type, the variable itself contains the value of the object rather than a reference to an object, modifying any field of the object via that variable is the same as modifying the variable, and is so a "modification via assignment", which is specifically prohibited by the specification.

This is exactly the same as if you had declared the value type variable as a field in another object, with the readonly modifier.

But note that this is a compile-time rule, enforced by the C# compiler, and that there's no way for the compiler to similarly enforce the rule for a value type that modifies itself.


I will point out that this is one of many excellent reasons that one should never ever implement a mutable value type. Mutable value types frequently wind up being able to be modified when you don't want them to be, while at the same time find themselves failing to be modified when you do want them to be (in completely different scenarios from this one).

If you treat a value type as something that is truly a value, i.e. a single value that is itself never changing, they work much better and find themselves in the middle of many fewer bugs. :)

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136