When should you use struct and not class in C#? My conceptual model is that structs are used in times when the item is merely a collection of value types. A way to
Structs are good for atomic representation of data, where the said data can be copied multiple times by the code. Cloning an object is in general more expensive than copying a struct, as it involves allocating the memory, running the constructor and deallocating/garbage collection when done with it.
Struct can be used to improve garbage collection performance. While you usually don't have to worry about GC performance, there are scenarios where it can be a killer. Like large caches in low latency applications. See this post for an example:
http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/
Structure types in C# or other .net languages are generally used to hold things that should behave like fixed-sized groups of values. A useful aspect of structure types is that the fields of a structure-type instance can be modified by modifying the storage location in which it is held, and in no other way. It's possible to code a structure in such a way that the only way to mutate any field is to construct a whole new instance and then use a struct assignment to mutate all the fields of the target by overwriting them with values from the new instance, but unless a struct provides no means of creating an instance where its fields have non-default values, all of its fields will be mutable if and if the struct itself is stored in a mutable location.
Note that it's possible to design a structure type so that it will essentially behave like a class type, if the structure contains a private class-type field, and redirects its own members to that of the wrapped class object. For example, a PersonCollection
might offer properties SortedByName
and SortedById
, both of which hold an "immutable" reference to a PersonCollection
(set in their constructor) and implement GetEnumerator
by calling either creator.GetNameSortedEnumerator
or creator.GetIdSortedEnumerator
. Such structs would behave much like a reference to a PersonCollection
, except that their GetEnumerator
methods would be bound to different methods in the PersonCollection
. One could also have a structure wrap a portion of an array (e.g. one could define an ArrayRange<T>
structure which would hold a T[]
called Arr
, an int Offset
, and an int Length
, with an indexed property which, for an index idx
in the range 0 to Length-1
, would access Arr[idx+Offset]
). Unfortunately, if foo
is a read-only instance of such a structure, current compiler versions won't allow operations like foo[3]+=4;
because they have no way to determine whether such operations would attempt to write to fields of foo
.
It's also possible to design a structure to behave a like a value type which holds a variable-sized collection (which will appear to be copied whenever the struct is) but the only way to make that work is to ensure that no object to which the struct holds a reference will ever be exposed to anything which might mutate it. For example, one could have an array-like struct which holds a private array, and whose indexed "put" method creates a new array whose content is like that of the original except for one changed element. Unfortunately, it can be somewhat difficult to make such structs perform efficiently. While there are times that struct semantics can be convenient (e.g. being able to pass an array-like collection to a routine, with the caller and callee both knowing that outside code won't modify the collection, may be better than requiring both caller and callee to defensively copy any data they're given), the requirement that class references point to objects that will never be mutated is often a pretty severe constraint.
Whenever you:
The caveat, however, is that structs (arbitrarily large) are more expensive to pass around than class references (usually one machine word), so classes could end up being faster in practice.
A struct is a value type. If you assign a struct to a new variable, the new variable will contain a copy of the original.
public struct IntStruct {
public int Value {get; set;}
}
Excecution of the following results in 5 instances of the struct stored in memory:
var struct1 = new IntStruct() { Value = 0 }; // original
var struct2 = struct1; // A copy is made
var struct3 = struct2; // A copy is made
var struct4 = struct3; // A copy is made
var struct5 = struct4; // A copy is made
// NOTE: A "copy" will occur when you pass a struct into a method parameter.
// To avoid the "copy", use the ref keyword.
// Although structs are designed to use less system resources
// than classes. If used incorrectly, they could use significantly more.
A class is a reference type. When you assign a class to a new variable, the variable contains a reference to the original class object.
public class IntClass {
public int Value {get; set;}
}
Excecution of the following results in only one instance of the class object in memory.
var class1 = new IntClass() { Value = 0 };
var class2 = class1; // A reference is made to class1
var class3 = class2; // A reference is made to class1
var class4 = class3; // A reference is made to class1
var class5 = class4; // A reference is made to class1
Structs may increase the likelihood of a code mistake. If a value object is treated like a mutable reference object, a developer may be surprised when changes made are unexpectedly lost.
var struct1 = new IntStruct() { Value = 0 };
var struct2 = struct1;
struct2.Value = 1;
// At this point, a developer may be surprised when
// struct1.Value is 0 and not 1
From the C# Language specification:
1.7 Structs
Like classes, structs are data structures that can contain data members and function members, but unlike classes, structs are value types and do not require heap allocation. A variable of a struct type directly stores the data of the struct, whereas a variable of a class type stores a reference to a dynamically allocated object. Struct types do not support user-specified inheritance, and all struct types implicitly inherit from type object.
Structs are particularly useful for small data structures that have value semantics. Complex numbers, points in a coordinate system, or key-value pairs in a dictionary are all good examples of structs. The use of structs rather than classes for small data structures can make a large difference in the number of memory allocations an application performs. For example, the following program creates and initializes an array of 100 points. With Point implemented as a class, 101 separate objects are instantiated—one for the array and one each for the 100 elements.
class Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Test
{
static void Main() {
Point[] points = new Point[100];
for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
}
}
An alternative is to make Point a struct.
struct Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
Now, only one object is instantiated—the one for the array—and the Point instances are stored in-line in the array.
Struct constructors are invoked with the new operator, but that does not imply that memory is being allocated. Instead of dynamically allocating an object and returning a reference to it, a struct constructor simply returns the struct value itself (typically in a temporary location on the stack), and this value is then copied as necessary.
With classes, it is possible for two variables to reference the same object and thus possible for operations on one variable to affect the object referenced by the other variable. With structs, the variables each have their own copy of the data, and it is not possible for operations on one to affect the other. For example, the output produced by the following code fragment depends on whether Point is a class or a struct.
Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);
If Point is a class, the output is 20 because a and b reference the same object. If Point is a struct, the output is 10 because the assignment of a to b creates a copy of the value, and this copy is unaffected by the subsequent assignment to a.x.
The previous example highlights two of the limitations of structs. First, copying an entire struct is typically less efficient than copying an object reference, so assignment and value parameter passing can be more expensive with structs than with reference types. Second, except for ref and out parameters, it is not possible to create references to structs, which rules out their usage in a number of situations.