A C# struct
stores its data in its type. It is not allocated separately on the managed heap—instead 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.
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.
struct
is created on the stack. No "new" keyword is used. It is used like a value type such as int
.struct
, in the same way that we can access a class
field.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
is a struct
. We create a DateTime
and then copy it into a separate DateTime
variable.DateTime
and its copy—the copy has all the same internal data as the original, but is separate.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
Next we see an example of using properties with the struct
type. Remember that your struct
cannot inherit like classes or have complex constructors.
struct
that simplify access to its data.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.
class
instance by using the new keyword. We can then access its public fields X and Y.struct
. We don't have to instantiate a struct
with the new keyword. We can directly access it.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
Consider a website that records referrer data. Each struct
here could store string
fields and a DateTime
field.
DateTime
itself is a struct
, it will be stored directly in the struct
allocation on the stack.struct
with 2 strings and a DateTime
, the struct
will hold 2 references and one value together.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. }; }
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.
KeyValuePair
struct
for storing a pair. It is used internally in the Dictionary
.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; } }
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.
CLRProfiler
indicates that the List
took 24 bytes and contained one object of 4.0 MB.Size of List: 1 object 512 KB Size of internal array: 100000 objects 3.8 MBSize of List: 1 object 24 bytes Size of internal array: 1 object 4.0 MB
Since they are simpler, structs should be faster to allocate than classes. We time the performance in .NET for each type.
class
in a tight loop and compute the average amount of time required.struct
that has the same fields as the class
. Each type has 8 fields.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
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
.
null
. In complex programs these can help simplify code.struct Test { int size; } class Program { public static void Main() { var s = new Test(); if (s == null) { } } }C:\...\Program.cs(11,13): error CS0019: Operator '==' cannot be applied to operands of type 'Test' and '<null>' The build failed. Fix the build errors and run again.
Structs are an advanced topic, but every C# developer has used them in value types. Custom structs often degrade performance, so should be typically avoided.