C# has a huge advantage over languages like Java and AS3: the struct. It allows us to create a value type with multiple members and avoid creating garbage every time we create one with new. At least that’s the simple version of the story. In reality, it’s really easy to accidentally create garbage with struct. Today’s article shows how to use struct so that you don’t create any garbage and reveals some of the traps so you won’t fall into them.

Let’s start with the sunny side of struct. Creating one and storing it as a local variable doesn’t create any garbage:

struct Struct
{
}
 
void Foo()
{
	var x = new Struct(); // no garbage created
}

You can also pass the local variable as a parameter to another function that takes that type of struct without creating any garbage:

void Foo()
{
	var x = new Struct(); // no garbage created
	Take(x); // no garbage created
}
 
void Take(Struct x)
{
}

None of this changes if the struct implements an interface:

interface Interface
{
}
 
struct Struct : Interface
{
}

But we start to run into problems when we mix these two. Passing the struct as a parameter to a function that takes the interface type suddenly creates garbage:

void Foo()
{
	var x = new Struct(); // no garbage created
	Take(x); // creates garbage!
}
 
void Take(Interface x)
{
}

Worse still, it creates garbage every time you call the function:

void Foo()
{
	var x = new Struct(); // no garbage created
	Take(x); // creates garbage!
	Take(x); // creates more garbage!
	Take(x); // creates even more garbage!
}
 
void Take(Interface x)
{
}

The same is true when the function takes a plain object instead of the struct or interface type:

void Foo()
{
	var x = new Struct(); // no garbage created
	Take(x); // creates garbage!
}
 
void Take(object x)
{
}

Update: This is incorrect because Nullable is a struct
It’s even worse when the function takes a nullable (?) parameter because an instance of Nullable is created behind the scenes:

void Foo()
{
	var x = new Struct(); // no garbage created
	Take(x); // creates even more garbage!
}
 
void Take(Struct? x)
{
}

When the function has a type parameter (i.e. uses generics), no garbage is created by calling the function:

void Foo()
{
	var x = new Struct(); // no garbage created
	Take(x); // no garbage created
}
 
void Take<T>(T x)
{
}

In all, that’s three ways to create garbage with struct and two ways to not create garbage. Just by the numbers, it’s easier to accidentally create garbage with struct than it is to avoid creating garbage. This means we need to keep our guard up when using struct and not just assume that it magically removes all garbage creation and GC problems. Make sure to pay special attention to the functions you call when using struct or you just might inadvertently create a bunch of garbage!

Do you know of any more ways to accidentally create garbage in C#? Let me know in the comments!