When is a class not a class? When it's a struct

Structs straddle the fence between basic and object types, having some features of each. Learn to effectively use this C# feature.

Conceptually, C# structs fit somewhere between value types like int and reference types like Object, having properties of both. From a programming standpoint, they're handy if you need to create a new objectlike grouping of behavior for what is essentially just a piece of data. In this article, I’ll introduce you to this strange denizen of the C# type system.

Value vs. reference
Like all .NET languages, C# has two kinds of types: value and reference. Value types, also known as primitive or basic types, have their memory allocated on the stack and represent their values directly in memory. Reference types, on the other hand, have their memory allocated from the dynamic memory heap and directly hold the address of their data in memory. The compiler can determine how much memory to allocate for each instance of a value type at runtime, but not for reference types, since they may be unpredictable in size.

A struct is C#’s way of allowing you to create new value types for use in your programs. For example, suppose I wanted to create a new numeric type based on the basic int type. I could define the following very simple struct to do so:
public struct NewInt{
public int Value;

Having thus declared NewInt, I could use it anywhere in my application by declaring a new instance of it and using the public Value field to store or retrieve the value I’m concerned with.
NewInt n = new NewInt();
n.Value = 42;

Classlike properties
Yes, you read that right; there’s no need to adjust your monitor. I did indeed use the same syntax I’d use to create a new object instance to create an instance of NewInt. But, being a value type, I shouldn’t be able to do that, right? So what gives?

Okay, here comes the confusing part. Although they are value types, structs have some features in common with classes, which are reference types. Like classes, they may have methods (both static and instance), implement interfaces, and use constructors. On the other hand, unlike classes, structs may not override their default parameterless constructor, explicitly inherit from a base class (they do implicitly inherit from System.Object), or be a base class themselves.

In practice, there are a number of other differences between classes and structs that you’ll need to be aware of:
  • ·        Structs may be passed only by value. The compiler will prevent you from passing a struct to a method by reference (as an out parameter). This makes a struct sent to a method effectively read-only.
  • ·        Assigning one struct variable to another creates a new copy of the original struct, not two pointers to the same struct.
  • ·        Structs may never legally be set to null.
  • ·        Creating a new struct instance is slightly faster than creating a new class instance.
  • ·        Each struct implicitly includes a parameterless constructor that takes care of initializing all the struct’s fields to their default values. While you may create your own parameterized constructor, you must ensure that all fields have a valid value before the constructor ends, and you may never override the default parameterless constructor.
  • ·        No fields in a struct can utilize initializers. It would have been an error to initialize the Value field of NewInt like this: int Value = 0;.

Here’s the final confusing tidbit: A struct may legally contain a reference type itself, in which case the implicit constructor will initialize it to null.

The MyDate example
Let’s wrap up our discussion with a little example. In Listing A, you’ll find the source for a small application that makes use of a struct called MyDate, which creates a simple new value type useful for storing dates. MyDate has three private int variables, one each for storing the month, day, and year of a given date. MyDate can hold a reference to a Calendar object to provide support for any custom formatting options I might need down the road. Although it’s redundant with the System.DateTime object and therefore wouldn’t be particularly useful, MyDate does provide a nice example of the possibilities of using a struct.


Editor's Picks