C# Struct Examples

Dot Net Perls

Array Collections File String Windows VB.NET Algorithm ASP.NET Cast Class Compression Convert Data Delegate Directive Enum Exception If Interface Keyword LINQ Loop Method .NET Number Regex Sort StringBuilder Struct Switch Time Value

Struct illustration

Objects live on the managed heap. Structs often reside on the evaluation stack. Objects occasionally cause slowdowns due to excessive heap allocations. Structs often reduce performance even more due to excessive stack usage. Every program uses simple structs. All value types—such as int, bool, and char—are structs.

Structs are particularly useful for small data structures that have value semantics. Hejlsberg et al., p. 537

Example

To start, we see a simple example program that uses a struct type in the C# programming language. This example console program uses a struct called Simple, which stores three values itself: two numbers and a Boolean.

Key points: Struct describes a value type in the C# language. Structs can improve speed and memory usage. They cannot be used in the same way as classes. Collections of structs are allocated together.

Program that declares struct [C#]

class Program
{
    struct Simple
    {
	public int Position;
	public bool Exists;
	public double LastValue;
    };

    static void Main()
    {
	Simple s;
	s.Position = 1;
	s.Exists = false;
	s.LastValue = 5.5;
    }
}

Result
    (The struct is created on the stack.)

Struct usages. As an aside, there are practical uses to structs and they are an important part of the language. Here's what the Visual Studio debugger shows inside the struct. Note that the struct is the type "Program.Simple".

Struct in Visual Studio debugger

Value types

Structs are custom value types that store the values in each field together. They do not store referenced data, such as the character array in a string. What MSDN says is that structs "do not require heap allocation." It says that variables of struct type "directly contain the data of the struct, whereas a variable of a class type contains a reference to the data."

MSDN reference 1

Struct type details. What that means is that with structs you avoid the overhead of objects in the C# language. You can combine multiple fields, reducing memory pressure and improving performance. Value semantics: this term indicates whether the variable is being used like numbers and values are, or as an inherited class. "Complex numbers, points in a coordinate system, or key-value pairs in a dictionary" are included.

Choosing structs

Only consider structs in performance-sensitive parts of your program. Points containing coordinates and positions are excellent examples of structs, as is the DateTime struct.

Small values. Many times in programs you have small classes that really serve as collections of related variables you store in memory. You don't have inheritance or polymorphism. These often make ideal structs.

Possible uses. Use structs for offsets. Structs are useful for storing coordinates of offsets in your files. These usually contain integers. Use structs for graphics. When using graphics contexts, use structs for points and coordinates. MSDN provides an example of a nullable integer used for databases. If you don't want to use Nullable<T>, use this.

MSDN reference 2

Properties

Here we look at an example of using properties with the struct type in the C# language. Remember that your struct cannot inherit like classes or have complex constructors. However, you can provide properties for it that simplify access to its data.

Program that uses property on struct [C#]

using System;

class Program
{
    static void Main()
    {
	// Initialize to 0.
	S st = new S();
	st.X = 5;
	Console.WriteLine(st.X);
    }

    struct S
    {
	int _x;
	public int X
	{
	    get { return _x; }
	    set
	    {
		if (value < 10)
		{
		    _x = value;
		}
	    }
	}
    };
}

Output

5

Stack versus heap allocation

Local value types are allocated on the stack. This includes integers such as "int i" for loops. When you create an object from a class in a function, it is allocated on the heap. The stack is much faster normally. The details of stacks and heaps are out of the scope here, but are worthwhile studying.

Struct type as parameters. Because structs are a value type and therefore inherit System.ValueType, they are copied completely when you pass them as parameters to methods. Therefore, structs will degrade performance when you pass them to methods, particularly if they are not very small.

Struct Versus Class

Change class to struct

You can't easily change classes that inherit or implement interfaces to structs. You cannot use a custom default constructor on structs, as when they are constructed, all fields are assigned to zero.

Class Examples
Version of program using class [C#]

class Program
{
    class C
    {
	public int X;
	public int Y;
    };

    static void Main()
    {
	C local = new C();
	local.X = 1;
	local.Y = 2;
    }
}

Version of program using struct [C#]

class Program
{
    struct C
    {
	public int X;
	public int Y;
    };

    static void Main()
    {
	C local;
	local.X = 1;
	local.Y = 2;
    }
}

Struct usage tip. You don't have to instantiate your struct with the new keyword. It works like an int, instead, which means you can access it directly without allocating it explicitly.

Null structs

Null keyword

You cannot compare a struct instance to the null literal. If this is confusing, you should think of structs as ints or bools. You can't set your integer variable to null. Nullable types, however, are a generic type that you can use with databases and declare null ints.

MSDN reference 3

Note: Notably, nullable types themselves (System.Nullable) are implemented with structs. The link to Database Integer Type is similar.

Nullable types

The C# programming language

The C# language provides a nullable type wrapper for any object. With the nullable wrapper, you can assign the null literal to even values like integers. We describe the nullable feature here.

Nullable Bool Nullable DateTime Nullable Int Nullable Memory Usage

Memory

Here we examine the memory usage of allocating many structs versus the memory usage of allocating many classes. I looked at the memory layout of the console program in the CLRProfiler. This is a free tool by Microsoft that visualizes the memory allocations of .NET programs.

First image. The first picture here is the memory profile of the version that uses classes. It indicates that the List took 512 KB and was one object, and internally it stored 100000 objects and took 3.8 MB.

Memory usage of classes

Second image. Using structs, CLRProfiler indicates that the List took 24 bytes and contained one object of 4.0 MB. That one object is an array of 100000 structures, all stored together.

Memory usage of structs
Class version

Size of List:           1 object
			512 KB
Size of internal array: 100000 objects
			3.8 MB

Struct version

Size of List:           1 object
			24 bytes
Size of internal array: 1 object
			4.0 MB

Results. What this means is that structs are not stored as separate objects in arrays, but are grouped together. This is possible because they are value types. We see that structs consume less memory.

Allocation performance

Let's look at how quickly the .NET runtime can allocate lots of classes versus how fast it can allocate many structs. It was easier to gather speed benchmarks of structs. I compared two classes to two structs. Both pairs of opposites use either eight ints or four strings.

Classes tested in benchmark [C#]

class S
{
    public int A;
    public int B;
    public int C;
    public int D;
    public int E;
    public int F;
    public int G;
    public int H;
}

class S
{
    public string A;
    public string B;
    public string C;
    public string D;
}

Structs tested in benchmark [C#]

struct S
{
    public int A;
    public int B;
    public int C;
    public int D;
    public int E;
    public int F;
    public int G;
    public int H;
}

struct S
{
    public string A;
    public string B;
    public string C;
    public string D;
}

Reminder. Recall that because strings are reference types, their internal data is not embedded in the struct. Just the reference or pointer is. I make note here that the performance benefits of structs with strings persists even when their referential data is accessed, as by assignment or appending.

Struct/class allocation performance timing

Class with 8 ints:               2418 ms
Struct with 8 ints:               936 ms [faster]

Class with 4 string references:  2184 ms
Struct with 4 string references:  795 ms [faster]

Benchmark notes: 10 million loops of 100000 iterations. Tests allocation speed of the data structures. Structs were much faster in both tests.

Results. We see substantial speedups of over two times when allocating the structs. This is because they are value types allocated on the stack. Note they are stored in a List field. I tried to ensure accuracy of the test by assigning each field and avoiding property accesses, which is why I use public fields.

String fields

String type

You can use string fields in struct types. Please note that the struct will not store the string's data. That will be stored externally, where the reference points. However, using structs can improve performance with the string reference itself. Remember that references are also data that need to be allocated, which we can use struct for.

ASP.NET website example

ASP.NET web programming framework

In my web project, I record thousands of referrer data objects. These store two string fields and a DateTime field. By hovering over DateTime in Visual Studio, you see that it too is a struct. Because DateTime itself is a struct, it will be stored directly in the struct allocation on the stack. Thus, in a struct with two strings and a DateTime, the struct will hold two references and one value together.

Program that uses struct in website [C#]

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
	var _d = new Dictionary<string, ReferrerInfo>();

	// New struct:
	ReferrerInfo i;
	i.OriginalString = "cat";
	i.Target = "mat";
	i.Time = DateTime.Now;

	_d.Add("info", i);
    }

    /// <summary>
    /// Contains information about referrers.
    /// </summary>
    struct ReferrerInfo
    {
	public string OriginalString; // Reference.
	public string Target;         // Reference.
	public DateTime Time;         // Value.
    };
}

Result
    (Dictionary of structs is created.)

Description. The optimization in the above code was to replace the class with a struct. This should improve performance by about two times and reduce memory.

File offset data

In a database system I developed, file blobs are stored in large files together, and I needed a way to store their offsets. Therefore I had structs with two members: two ints storing positions. Structs are ideal for this situation: there were over 500 instances of the object, and they only had two member fields of value types.

Program that uses Dictionary of structs [C#]

using System.Collections.Generic;

class Program
{
    static void Main()
    {
	// Stores Dictionary of structs.
	var _d = new Dictionary<string, FileData>();
	FileData f;
	f.Start = 1000;
	f.Length = 200;
	_d.Add("key", f);
    }

    /// <summary>
    /// Stores where each blob is stored.
    /// </summary>
    struct FileData
    {
	public int Start;
	public int Length;
    }
}

Result
    (Dictionary of structs is created.)

Pointers and structs

Star (asterisk) character

The C# language frees us developers from the nightmare that is C pointers, but understanding pointers is important in performance and language work. Pointers, like references, are values that contain the addresses of data. C-style pointers are blisteringly fast, but their syntax and lack of error checking causes problems. However, the struct keyword gives us more power over references and how fields are stored.

ValueType

.NET Framework information

In the .NET Framework, values such as integers are stored in structs. However, the ValueType class is an abstract base class for these values. You can refer to these values through the ValueType class.

ValueType

Fixed buffers

Unsafe code is just that: not safe, but still usually functional. It can also be very fast in some cases. The struct type can be used with fixed buffers as a memory region; this can simplify some low-level code.

Fixed Buffer Struct (Unsafe)

Summary

The C# programming language

We saw ways you can use structs in your C# programs. Though structs are an advanced topic, nearly every developer has used them in value types such as int or DateTime. By understanding the value semantics in the C# language, we are lead to probably the most important performance optimization with structs: avoid them except in certain contexts.