It seems to me that one of the most underused features of the C# (or Visual Basic) language is the structure. Developers are often lead to believe that use of classes yield performance benefits. While this is often the case, structures have their own set of advantages that, when used in the right situation, will yield important, and sometimes substantial performance benefits. This blog post is intended to help demystify some of the confusions between structures and classes.
In their simplest form, classes and structures in C# are very similar. In fact, often times, you may simply replace the class keyword with the struct keyword, and your classes can instantly be converted into a struct:
public class Person
{
}
public struct Person
{
}
Indeed, from the standpoint of a text editor, these two entities are identical except for the class and struct keywords. At the system level, however, these two entities behave very differently. As most C# developers know, the biggest difference between the two entities is that the class is a reference type, whereas the struct is a value type. A slightly more educated C# developer would also point out that the struct is stored in the
stack, whereas the class is stored in the
heap. This is an important concept to understand because it is this reason that structures have the potential for increased performance in some situations, but also one of their biggest drawbacks, in other situations.
By being stored in the stack, the runtime (CLR) can create, read, update and remove value types very quickly, with minimal overhead. It is important to note, however, that the stack is limited. In fact, you should never create a value type with an instance size greater than 128 bits (16 bytes).
In contrast to value types, reference types store a pointer in the stack that points to an object found in the heap. By storing reference types in the heap, the amount of memory is limited to the available physical RAM found in the computer that the code is executing on (there is, of course, the concept of swapping, which increases the theoretical limit of available memory; however, that goes beyond the scope of this article). Following is a simple example of objects types stored in the heap (please excuse the artwork, I'm certainly not a designer):
From looking at this simple diagram, it should be obvious that accessing something from the stack can be faster than accessing something from the heap. This is because the processor has direct access to something stored on the stack, whereas it has to do a lookup and then 'fetch" of anything found on the heap. There is actually more to it, but for the sake of simplicity, I'll leave it at that.
Another important concept to realize is that, because they are stored in the stack, value types create a second copy of the value when they are copied. That is, the stack will contain two or more instances of the value each time it is copied. Reference types, on the other hand, are not copied. Instead, only the pointer is copied. In other words, when you copy a reference type, the object itself is not copied, only the pointer is copied. This obviously has its benefits; especially for large objects.
So then, the important question is, when should you use a structure versus a class? Following is a simple list of requirements that should help you decide when to use a structure.
- Represents a single value
- Will not be changed after its creation
- Will not be cast to a reference type (for more information, read up on boxing and unboxing)
- Has an instance size less than 128 bits (16 bytes)
If your entity does not meet those requirements, your best choice is going to be the use of a class.
For more information on the differences between classes and structures, be sure to check out
Microsoft's page.
Update:
Rick Minerich has posted a terrific series of articles related to memory management. I highly recommend reading all three, but for those of you that would like to jump straight ahead to managing the size of the stack, please jump to part 3:
Part 1 – Basic Housekeeping
Part 2 – Improving Performance Through Stack Allocation
Part 3 – Increasing the Size of your Stack