Mutable structs are the only data type in .net that can implement mutable semantics of values. Some people, such as Eric Lippert, hate them because they make life harder for compiler authors, but the semantics they offer are often much clearer than those available with reference types.
For example, suppose one has a class and structure type, as well as an interface as follows:
struct myStruct {public int v; ... other stuff ...};
class myClass {public int v; ... other stuff ...};
interface ISample {
void useStructByValue (myStruct z);
void useStructByReference (ref myStruct z);
void useClassByValue (myClass z);
void useClassByReference (ref myClass z);
}
and consider the following method:
void test (myStruct struct1, myStruct struct2, myClass class1, myClass class2,
ISample munger)
{
for (int i = 0; i <5; i ++)
{
munger.useStructByValue (struct1); // S1
munger.useStructByReference (ref struct2); // S2
munger.useClassByvalue (class1); // S3
munger.useClassByReference (ref class2); // S4
}
}
Assuming munger is not using any insecure code, which of the elements passed may have a .v field affected by which of the four statements? I would suggest that without even seeing the whole definition of struct1 or any part of the munger implementation, we can say that struct1.v will not be modified by any of them. Guaranteed. In addition, struct2.v can be changed to S2, but not by any other statement. Again, guaranteed. However, the values ββof class1.v and class2.v can be changed by any of the operators; the only way to find out which operator class1.v or class2.v can change is to study the code for each individual type that will ever, or in the future, implement ISample.
In other words, structures contain semantics that are limited but clearly defined. Classes do not work.
By the way, due to limitations in the operation of properties, you cannot directly modify or pass by reference the property fields of the structure. However, code like:
List <myStruct> myList;
...
myStruct tempStruct = myList [1];
tempStrict.v = 5;
myList [1] = tempStruct;
it is not thread safe yet, it has clear and obvious semantics. Replace myClass for myStruct, and confidence exits the window. If each list item has an unshared instance of myClass, you can simply say myList[1].v = 5; Nice and comfortable. Unfortunately, determining whether a list item is a unique instance of myClass is almost impossible. If someone, trying to copy the values ββin myList[1] to myList[0] , said something like myList[0] = myList[1]; , such an operator would work, but as a result, a subsequent entry in myList[1].v also affect myList[0].v . Shockingly nasty. Structures would not have this problem.