.NET Type Design Guidelines—Choosing Between Class and Struct
| CSharp-Online.NET:Articles |
| .NET Articles |
| © 2006 Microsoft Corp. |
Choosing Between Class and Struct
One of the basic design decisions every framework designer faces is whether to design a type as a class (a reference type) or as a struct (a value type). Good understanding of the differences in the behavior of reference types and value types is crucial in making this choice.
Reference types are allocated on the heap, and garbage-collected, whereas value types are allocated either on the stack or inline in containing types and deallocated when the stack unwinds or when their containing type gets deallocated. Therefore, allocations and deallocations of value types are in general cheaper than allocations and deallocations of reference types.
Arrays of reference types are allocated out-of-line, meaning the array elements are just references to instances of the reference type residing on the heap. Value type arrays are allocated in-line, meaning that the array elements are the actual instances of the value type. Therefore, allocations and deallocations of value type arrays are much cheaper than allocations and deallocations of reference type arrays. In addition, in a majority of cases value type arrays exhibit much better locality of reference.
| RICO MARIANI |
| The preceding is often true but it's a very broad generalization that I would be very careful about. Whether or not you get better locality of reference when value types get boxed when cast to an array of value types will depend on how much of the value type you use, how much searching you have to do, how much data reuse there could have been with equivalent array members (sharing a pointer), the typical array access patterns, and probably other factors I can't think of at the moment. Your mileage might vary but value type arrays are a great tool for your toolbox. |
Value types get boxed when cast to object or one of the interfaces they implement. They get unboxed when cast back to the value type. Because boxes are objects that are allocated on the heap and are garbage collected, too much boxing and unboxing can have a negative impact on the heap, the garbage collector, and ultimately the performance of the application.
Reference type assignments copy the reference, whereas value type assignments copy the entire value. Therefore assignments of large reference types are cheaper than assignments of large value types.
Finally, reference types are passed by reference, whereas value types are passed by value. Changes to an instance of a reference type affect all references pointing to the instance. Value type instances are copied when they are passed by value. When an instance of a value type is changed, it of course does not affect any of its copies. Because the copies are not created explicitly by the user, but rather implicitly when arguments are passed or return values are returned, value types that can be changed can be confusing to many users. Therefore value types should be immutable.1
| RICO MARIANI |
| If you make your value type mutable you will find that you end up having to pass it by reference a lot to get the semantics you want (using, e.g., "out" syntax in C#). This might be important in cases in which the value type is expected to be embedded in a variety of other objects that are themselves reference types or embedded in arrays. The biggest trouble from having mutable value types is where they look like independent entities like, for example, a complex number. Value types that have a mission in life of being an accumulator of sorts or a piece of a reference type have fewer pitfalls for mutability. |
As a rule of thumb, the majority of types in a framework should be classes. There are, however, some situations in which the characteristics of a value type make it more appropriate to use structs.
- CONSIDER defining a struct instead of a class if instances of the type are small and commonly short-lived or are commonly embedded in other objects.
- DO NOT define a struct unless the type has all of the following characteristics:
- It logically represents a single value, similar to primitive types (
int,double, etc.).
- It logically represents a single value, similar to primitive types (
- It has an instance size under 16 bytes.
- It is immutable.
- It will not have to be boxed frequently.
In all other cases, you should define your types as classes.
| JEFFREY RICHTER |
| In my opinion, a value type should be defined for types that have approximately 16 bytes or less. Value types can be more than 16 bytes if you don't intend to pass them to other methods or copy them to and from a collection class (like an array). I would also define a value type if you expect instances of the type to be used for short periods of time (usually they are created in a method and no longer needed after a method returns). I used to discourage defining value types if you thought that instances of them would be placed in a collection due to all the boxing that would have to be done. But, fortunately, newer versions of the CLR, C#, and other languages support generics so that boxing is no longer necessary when putting value type instances in a collection. |
|

