In C#, if you have a struct
like so:
struct Counter
{
private int _count;
public int Value
{
get { return _count; }
}
public int Increment()
{
return ++_count;
}
}
And you have a program like so:
static readonly Counter counter = new Counter();
static void Main()
{
// print the new value from the increment function
Console.WriteLine(counter.Increment());
// print off the value stored in the item
Console.WriteLine(counter.Value);
}
The output of the program will be:
1
0
This seems completely wrong. I would either expect the output to be two 1s (as it is if Counter
is a class
or if struct Counter : ICounter
and counter
is an ICounter
) or be a compilation error. I realize that detecting this at compilation time is a rather difficult matter, but this behavior seems to violate logic.
Is there a reason for this behavior beyond implementation difficulty?
structs
are value types and therefore have a value type sematics. This means each time you access the struct you basically work with a copy of the struct's value.In your sample you don't change the original
struct
but only a temporary copy of it.See here for further explanations:
In .net, a struct instance method is semantically equivalent to a static struct method with a an extra
ref
parameter of the struct type. Thus, given the declarations:The method calls:
are semantically equivalent, except for one difference: the latter call will only be permitted if
someBlah
is a mutable storage location (variable, field, etc.) and not if it is a read-only storage location, or a temporary value (result of reading a property, etc.).This faced the designers of .net languages with a problem: disallowing the use of any member functions on read-only structs would be annoying, but they didn't want to allow member functions to write to read-only variables. They decided to "punt", and make it so that calling an instance method on a read-only structure will make a copy of the structure, invoke the function on that, and then discard it. This has the effect of slowing down calls to instance methods which do not write the underlying struct, and making it so that an attempt to use a method which updates the underlying struct on a read-only struct will yield different broken semantics from what would be achieved if it were passed the struct directly. Note that the extra time taken by the copy will almost never yield correct semantics in cases which would not have been correct without the copy.
One of my major peeves in .net is that there is still (as of at least 4.0, and probably 4.5) still no attribute via which a struct member function can indicate whether it modifies
this
. People rail about how structs should be immutable, rather than providing the tools to allow structs to safely offer mutating methods. This, despite the fact that so-called "immutable" structs are a lie. All non-trivial value types in mutable storage locations are mutable, as are all boxed value types. Making a struct "immutable" may compel one to rewrite a whole struct when one only wants to change one field, but sincestruct1 = struct2
mutates struct1 by copying all the public and private fields from struct2, and there's nothing the type definition for the struct can do to prevent that (except not have any fields) it does nothing to prevent unexpected mutation of struct members. Further, because of threading issues, structs are very limited in their ability to enforce any sort of invariant relationship among their fields. IMHO, it would generally be better for a struct with to allow arbitrary field access, making clear that any code receiving a struct must check whether its fields meet all required conditions, than try to prevent the formation of structs which don't meet conditions.