Home
C#
struct Examples
This page was last reviewed on Apr 26, 2023.
Dot Net Perls
Struct. A C# struct stores its data in its type. It is not allocated separately on the managed heap. Structs often reside on the evaluation stack.
Every program uses simple structs. All value types (int, bool, char) are structs. We rarely benefit from creating custom structs.
First example. A struct uses syntax similar to a class. It is a type definition. We describe a struct called Simple: this struct stores 3 values—an int, a bool and a double value.
Step 1 The struct is created on the stack. No "new" keyword is used. It is used like a value type such as int.
Step 2 We can access a field from a struct, in the same way that we can access a class field.
class
Info The struct has the composite name "Program.Simple": it is nested within the Program class.
using System; class Program { struct Simple { public int Position; public bool Exists; public double LastValue; }; static void Main() { // Step 1: create struct on stack. Simple s; s.Position = 1; s.Exists = false; s.LastValue = 5.5; // Step 2: write struct field. Console.WriteLine(s.Position); } }
1
DateTime. DateTime is a value that indicates a point in time. This is a struct. Positions, coordinates and points can sometimes benefit from being structs.
DateTime
Step 1 We see that DateTime is a struct. We create a DateTime and then copy it into a separate DateTime variable.
Step 2 We print the values of the DateTime and its copy—the copy has all the same internal data as the original, but is separate.
Step 3 We change the original DateTime. Here the copy remains the same—it is kept separate.
using System; class Program { static void Main() { // Step 1: create Date Time and copy it. DateTime date = new DateTime(2000, 1, 1); DateTime dateCopy = date; // Step 2: show struct values. Console.WriteLine(date); Console.WriteLine(dateCopy); // Step 3: see that copy is not modified. date = DateTime.MinValue; Console.WriteLine(dateCopy); } }
1/1/2000 12:00:00 AM 1/1/2000 12:00:00 AM 1/1/2000 12:00:00 AM
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.
Tip You can provide properties for the struct that simplify access to its data.
Property
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; } } } }; }
5
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.
Part 1 We create a class instance by using the new keyword. We can then access its public fields X and Y.
new
Part 2 We create a struct. We don't have to instantiate a struct with the new keyword. We can directly access it.
Info You cannot use a custom default constructor on structs, as when they are constructed, all fields are assigned to zero.
using System; class Program { class C { public int X; public int Y; } struct CStruct { public int X; public int Y; } static void Main() { // Part 1: use class. C local = new C(); local.X = 1; local.Y = 2; Console.WriteLine("CLASS X: {0}", local.X); // Part 2: use struct. CStruct localStruct; localStruct.X = 1; localStruct.Y = 2; Console.WriteLine("STRUCT X: {0}", localStruct.X); } }
CLASS X: 1 STRUCT X: 1
Structs, ASP.NET. Consider a website that records referrer data. Each struct here could store string fields and a DateTime field.
And Because DateTime itself is a struct, it will be stored directly in the struct allocation on the stack.
Thus In a struct with 2 strings and a DateTime, the struct will hold 2 references and one value together.
Detail We replaced the class with a struct. This should improve performance by about 2 times and reduce memory.
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. }; }
Structs, offsets. In a database system, file blobs are stored in large files together. We can store their offsets instructs that have 2 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).
Detail We can use the KeyValuePair struct for storing a pair. It is used internally in the Dictionary.
KeyValuePair
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; } }
Memory usage. Suppose we have a List of class instances, and a List of structs. The class instances will all be separate objects, but the structs can all be stored together.
List
Info Using structs, CLRProfiler indicates that the List took 24 bytes and contained one object of 4.0 MB.
Tip Structs are not stored as separate objects in arrays, but are grouped together. We see that structs consume less memory.
Size of List: 1 object 512 KB Size of internal array: 100000 objects 3.8 MB
Size of List: 1 object 24 bytes Size of internal array: 1 object 4.0 MB
Benchmark, allocation. Since they are simpler, structs should be faster to allocate than classes. We time the performance in .NET for each type.
Benchmark
Version 1 In this version of the code, we allocate a class in a tight loop and compute the average amount of time required.
Version 2 Here we allocate a struct that has the same fields as the class. Each type has 8 fields.
Result We see substantial speedups when allocating the structs. For pure allocation speed, structs are faster.
Important Strings are reference types. Their internal data is not embedded in the struct—it just stores the reference.
using System; using System.Diagnostics; class AllocateClass { public string A; public string B; public string C; public string D; public int E; public int F; public int G; public int H; } struct AllocateStruct { public string A; public string B; public string C; public string D; public int E; public int F; public int G; public int H; } class Program { const int _max = 10000000; static void Main() { var s1 = Stopwatch.StartNew(); // Version 1: allocate class. for (int i = 0; i < _max; i++) { AllocateClass test = new AllocateClass(); test.E = 10; if (test.E != 10) { return; } } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: allocate struct. for (int i = 0; i < _max; i++) { AllocateStruct test; test.E = 10; if (test.E != 10) { return; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } }
4.28 ns Allocate class 0.98 ns Allocate struct
Notes, value types. Structs are custom value types that store the values in each field together. They do not store referenced data, but can store references.
Tip Heap allocation is used for the data of arrays and objects. Structs, meanwhile, have all their data stored inline.
Info With structs you avoid the overhead of objects. This reduces memory pressure. And it (sometimes) improves performance.
Detail The term "value semantics" refers to how a struct has all its data inline, and is not stored in a separate location on the heap.
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).
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.
Parameters. This is important: structs are copied when you pass them as parameters to methods. Structs can therefore degrade performance when passed to methods.
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
Strings. You can use string fields instruct 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.
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
A summary. Structs are an advanced topic. But every developer has used them in value types. Custom structs often degrade performance, so should be typically avoided.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Apr 26, 2023 (edit).
Home
Changes
© 2007-2024 Sam Allen.