structs and 'using'

N

Nemanja Trifunovic

A question for C# language experts:

If I have a struct that implements IDisposable:

struct C : IDisposable
{
public int clan;
public void Dispose()
{
Console.WriteLine("Disposing");
}
}


And later in the code something like:
using (C s = new C())
{
s.clan = 1;
}


A compiler error is reported:
error CS0131: The left-hand side of an assignment must be a variable,
property or indexer

Why does that happen?
 
G

Guest

the struct is being boxed I believe, therefore the error is generated because
you'd never been able to assign value to the original struct with that line
of code.
 
D

Daniel Pratt

The struct will indeed get boxed when the "using" statement gets a reference
to the IDisposable interface. On the other hand, variable "s" should be the
original unboxed struct and therefore it should be possible to do what is
being done. I say it's a bug. Especially since this will work:

C s = new C();

using (s)
{
s.clan = 1;
}

Regards,
Daniel
 
J

John Puopolo

Hmm...

I do not have a quick answer to this. To test this, I wrote a simple
program and disassembled it. The code should expand to the following:

C s;
s = new C();
try { s.clan =1 } finally { s.Dispose(); }

If you hand-code this instead of leveraging the "using" statement, this code
compiles fine. There must be something to the fact that C is a struct vs. a
class (try changing "stuct" to "class" to see everything work fine in your
original). In addition, even if you do not inherit from IDispose (or
anything else, for that matter and leave the struct a plain vanilla struct),
the compiler still complains.

I'd be interested in the reply as well...

John
 
N

Nicholas Paldino [.NET/C# MVP]

Daniel and Daniel,

The structure does not get boxed. The compiler realizes that the
structure has a public Dispose method and calls it directly on the
structure. It is never boxed.

The reason for this is that certain constructs cause the object
mentioned in them to be readonly. The using statement and the foreach
statement are the two offhand that I remember in C# that you can not change.
For most reference types, if you do something like this:

using (MyClass s = new MyClass())
{

}

You can change properties of s, but you can't change s itself. Because
you are using a structure, and it is not a reference, it is not allowed to
be changed.

Hope this helps.
 
N

Nicholas Paldino [.NET/C# MVP]

John,

The fact that it is only a structure is part of the reason. The target
of a "using" statement and a "foreach" statement can not be modified. In
the case of a reference type, that means the reference. In the case of a
value type, that means the whole structure.

By "target", I mean the instance that is created in the statement. If
you are using an existing variable, then the restriction does not apply.
 
D

David Browne

Daniel Pratt said:
The struct will indeed get boxed when the "using" statement gets a
reference
to the IDisposable interface. On the other hand, variable "s" should be
the
original unboxed struct and therefore it should be possible to do what is
being done. I say it's a bug. Especially since this will work:

C s = new C();

using (s)
{
s.clan = 1;
}

First of all, why would you ever have a Disposable struct?

But putting that aside, this is not a bug.

From the C# Language Specification
http://msdn.microsoft.com/library/d...ry/en-us/csspec/html/vclrfcsharpspec_8_13.asp

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 (by assignment or the ++
and -- operators) or pass them as ref or out parameters.

This works fine:
 
G

Guest

as always, you are right Nick. I was just thinking, it wouldn't really had
made sense that a value type will be boxed when an method is called, like
DateTime.AddXXX. so I looked in the IL and there's no box.

Nicholas Paldino said:
Daniel and Daniel,

The structure does not get boxed. The compiler realizes that the
structure has a public Dispose method and calls it directly on the
structure. It is never boxed.

The reason for this is that certain constructs cause the object
mentioned in them to be readonly. The using statement and the foreach
statement are the two offhand that I remember in C# that you can not change.
For most reference types, if you do something like this:

using (MyClass s = new MyClass())
{

}

You can change properties of s, but you can't change s itself. Because
you are using a structure, and it is not a reference, it is not allowed to
be changed.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Daniel Pratt said:
The struct will indeed get boxed when the "using" statement gets a
reference
to the IDisposable interface. On the other hand, variable "s" should be
the
original unboxed struct and therefore it should be possible to do what is
being done. I say it's a bug. Especially since this will work:

C s = new C();

using (s)
{
s.clan = 1;
}

Regards,
Daniel
 
N

Nicholas Paldino [.NET/C# MVP]

David,

I would never implement Dispose on a struct, but I would have a struct
that has a public dispose method to do scope cleanup. For example, I have a
struct that performs an operation like a data adapter, where it will take a
connection, and if it is closed, will close it, and if open, will keep it
open. When it disposes, it will leave the connection state the way it found
it.

It is placed in a struct specifically to be used in a using statement as
a perf enhancement. Having an object do this would require the object to be
cleaned up at a later time, instead of being wiped away with the stack.
 
G

Guest

Daniel Pratt said:
The struct will indeed get boxed when the "using" statement gets a reference
to the IDisposable interface. On the other hand, variable "s" should be the
original unboxed struct and therefore it should be possible to do what is
being done. I say it's a bug. Especially since this will work:

C s = new C();

using (s)
{
s.clan = 1;
}

this works because apparently right after initobj of 's', a second copy is
made, and Dispose method is called off of this second copy, leaving the
original copy of 's' free to be modified.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top