C# Struct Examples

Use structs and measure performance. Structs are value types and types like int, bool and double are structs.
Struct. A struct stores its data in its type. It is not allocated separately on the managed heap. Structs often reside on the evaluation stack.
Consider this: every program uses simple structs. All value types (int, bool, char) are structs. We rarely benefit from creating custom structs.Int, uintBoolChar
Example. A struct uses syntax similar to a class. It is a type definition. We describe a struct called Simple: this struct stores three values, including an int, a bool and a double value.Class

Main: The struct is created on the stack. No "new" keyword is used. It is used like a value type such as int.

Program.Simple: The struct has the composite name "Program.Simple": it is nested within the Program class.

Debugger: In the Visual Studio debugger, you can inspect variables like the struct in this program.

C# program that declares struct using System; class Program { struct Simple { public int Position; public bool Exists; public double LastValue; }; static void Main() { // ... Create struct on stack. Simple s; s.Position = 1; s.Exists = false; s.LastValue = 5.5; // ... Write struct field. Console.WriteLine(s.Position); } } Output 1
Values. 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.

Tip: Heap allocation is used for the data of arrays and objects. Microsoft says that structs "do not require heap allocation."

Further: Variables "directly contain the data of the struct." And "a variable of a class type contains a reference to the data."

Values, continued. With structs you avoid the overhead of objects in the C# language. You can combine multiple fields. This reduces memory pressure. And it (sometimes) improves performance.

Value semantics: This term indicates whether the variable is being used like numbers and values are, or as an inherited class.

Note: Microsoft states that "complex numbers, points in a coordinate system, or key-value pairs in a dictionary" are included.

Usage. Structs should be considered in performance-sensitive parts of a program. Points containing coordinates and positions are excellent examples of structs, as is the DateTime struct.DateTime

Here: We demonstrate that a DateTime is a struct. We create a DateTime and then copy it into a separate DateTime variable.

Tip: When the original changes the copy remains the same. The memory is kept separate.

C# program that uses DateTime struct using System; class Program { static void Main() { // DateTime is a struct. DateTime date = new DateTime(2000, 1, 1); // When you assign a DateTime, a separate copy is created. DateTime dateCopy = date; // The two structs have the same values. Console.WriteLine(date); Console.WriteLine(dateCopy); // The copy is not affected when the original changes. date = DateTime.MinValue; Console.WriteLine(dateCopy); } } Output 1/1/2000 12:00:00 AM 1/1/2000 12:00:00 AM 1/1/2000 12:00:00 AM
Usage, small classes. Often in programs we have small classes that serve as collections of related variables stored in memory. Here we require no inheritance or polymorphism.

Tip: These types often make ideal structs. But even here, structs may make things worse (often because of parameter passing).

Usage, offsets. Structs are useful for storing coordinates of offsets in your files. These usually contain integers. It is also worthwhile to use structs for graphics.

Points: When using graphics contexts, we use structs for points and coordinates.

Property. Next we see an example of using properties with the struct type. Remember that your struct cannot inherit like classes or have complex constructors.

But: You can provide properties for the struct that simplify access to its data.

C# program that uses property on struct 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, heap. Locals are allocated on the stack. This includes ints and object references. When we create an object from a class, it is allocated on the heap. The stack is normally faster.

Note: The details of stacks and heaps are out of the scope here, but are worthwhile studying.

Parameters. This is important. Structs are copied when you pass them as parameters to methods. Structs can therefore degrade performance when passed to methods.Struct vs. Class
Class. You can't easily change many classes (ones that inherit or implement interfaces) to structs. For simple classes, we can specify a struct to test performance.

Also: You cannot use a custom default constructor on structs, as when they are constructed, all fields are assigned to zero.

Tip: You don't have to instantiate your struct with the new keyword. It instead works like an int—you can directly access it.

C# program that uses class class Program { class C { public int X; public int Y; }; static void Main() { C local = new C(); local.X = 1; local.Y = 2; } } C# program that uses struct class Program { struct C { public int X; public int Y; }; static void Main() { C local; local.X = 1; local.Y = 2; } }
Null. You cannot compare a struct instance to the null literal. If this is confusing, think of structs as ints or bools. You can't set your integer variable to null.

Note: Nullable types are a generic type that can be null. In complex programs these can help simplify code.

Nullable types. 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.Nullable
Memory. 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.

CLRProfiler: This is a free tool by Microsoft that visualizes the memory allocations of .NET programs.

Memory, classes. This is the memory profile of the version that uses classes. The List took 512 KB and was one object. Internally the List stored 100000 objects and required 3.8 MB.List

Note: Sorry for the hard-to-read image. You can just my word for the results (or try the benchmark yourself).

Memory, structs. 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.

Tip: Structs are not stored as separate objects in arrays, but are grouped together. We see that structs consume less memory.

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
Allocation speed. We allocate lots of classes. Then we allocate lots of structs. We time the performance of the .NET Framework for each type.

Note: It was easier to gather speed benchmarks of structs. I compared two classes to two structs.

Note 2: Both pairs of opposites, class S and struct S, 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; }
Allocation speed, continued. Strings are reference types. Their internal data is not embedded in the struct. Just the reference or pointer is.

Thus: The performance benefits of structs with strings persists even when their referential data is accessed.

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

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]
Allocation speed, analysis. In this test, we see substantial speedups of over two times when allocating the structs. Note they are stored in a List field.

Also: I tried to ensure accuracy of the test by assigning each field and avoiding property accesses.

Strings. You can use string fields in struct types. The struct will not store the string's data. Strings are objects, so the data will be stored externally, where the reference points.

However: Using structs can improve performance with the string reference itself. References are also data.

ASP.NET. In an old project, I recorded referrer data. These store two string fields and a DateTime field. By hovering over DateTime in Visual Studio, you see that it too is a struct.

And: 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.

Example: We replaced the class with a struct. This should improve performance by about two times and reduce memory.

However: Some benefits depend on usage. If the type is often passed to methods, this optimization could reduce speed.

C# program that uses struct in website 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. }; }
Offsets. In a database system, file blobs are stored in large files together. I needed a way to store their offsets. Therefore we can use a struct with two members—ints storing positions.

And: Structs were ideal here. There were over 500 instances of the object. And they only had two fields (both value types).

KeyValuePair: The .NET Framework provides the KeyValuePair struct for storing a pair. It is used internally in the Dictionary.

C# program that uses Dictionary of structs 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; } }
Pointers. The C# language frees us from the complexity of pointers and memory allocation. But understanding pointers is important.

Tip: Pointers, like references, are values that contain the addresses of data. When used correctly, pointers are fast.

But: Their syntax and lack of error checking causes problems. They are harder to use than references.

Note: The struct keyword gives us more power over references and how fields are stored.

Pointers, unsafe. Pointers are used with structs in unsafe code. Unsafe code is just that: not safe, but still usually functional. It is sometimes faster.

Fixed buffers: The struct type can be used with fixed buffers as a memory region.

ValueType. Values such as integers are stored in structs. The ValueType class is an abstract base class for these values. You can refer to them with a ValueType reference.ValueType Class
A summary. Structs are an advanced topic. But every developer has used them in value types. By understanding value semantics, we learn an important performance optimization.
A final caution. Structs often degrade performance, so should be typically avoided. More important optimizations, like using Dictionary or List correctly, are often more helpful.
© 2007-2020 Sam Allen. Every person is special and unique. Send bug reports to
Dot Net Perls