Underappreciated C# Struct

Matteo Tosato
4 min readNov 23, 2021

feature image

Skilled enterprise developers doesn’t use structs in their code. — Copying struct data because of pass-by-value can outweigh performance improvement we gained by avoiding heap allocation — .

How much truth is there ?

Allocation benchmark
Allocation benchmark

If you look at the Common Intermediate Language code of the snippet above, immediately noticed the difference in allocation.

In case of struct, we find an initobj instruction:

IL_0001: ldloca.s sd
IL_0003: initobj SomeStruct

Memory region under address taken from evaluation stack is initialized as “SomeStruct” (initialized each field of the value type at a specific address to a null reference or a 0).

In case of class, we find a newobj instruction:

IL_0009: newobj instance void SomeClass::.ctor()

Allocator is being called creating a new instance of “SomeClass” object and the reference to it is pushed onto the evaluation stack.

struct vs class allocation
Struct vs Class allocation timing

Struct allocation is faster since it uses usually only the stack.
Another advantage is about memory consumption. Class allocation uses more space since it puts type information for Garbage Collector along the data itself.

Furthermore, small struct data (usually less than 24 bytes) may be nicely optimized by the JIT compiler to use only CPU registers.

These aspects make using struct seriously considerable when writing data intense and performance oriented code and becomes crucial when operations number or amount of data grows considerably

Immutability enhanced

Anyway, struct downside is his wrong usage. Noteworthy points can be summarized in:

  • Copy-by-value semantics: poor performance of data copying when used as method parameters.
  • Behavioral limitations: no inheritance is possible.
  • Fields assignment when passing around your object: One may have the impression that modifying it will modify its original value.

Best practices to use struct data may help in avoiding unexpected behaviors, can be summed up in enhancing their usage constraints.

A well-known workaround is based on passing struct data by reference with ref keyword. And, in general, explicitly state that an object should not be modified by making it immutable or emphasizing his immutability.

Struct fields immutability by design
Struct fields immutability by design
Avoiding copy-by-value semantics
Avoiding copy-by-value semantics

Garbage Collection impact

Stack allocation doesn’t have Garbage Collection information, data lives as much as the function scope and avoids overhead of managing such type instance by the GC. Structs have a good data locality — we can pack data more dense — and needs less memory than classes.

Those traits make structs relevant when working with caches.

Cache class
Cache class
cache items
Cache items

Item has Price class elements. For testing purposes, we are going to insert many Price objects per item to simulate a considerable cache usage while measures the GC timing.

Test routine
Test routine
GC timing with cache by class objects
GC timing with cache by class objects

Now let’s modify the Price object and turn it into a struct, we’ll get a quite different result. GC is not a free feature!

GC timing with cache by struct objects
GC timing with cache by struct objects

Struct-based cache is much more faster than class-based one.

Structures are probably one of the most underestimated elements of C# but their were not invented without a reason, small data structs proves to be very useful in performance oriented tasks.

Sign up to discover human stories that deepen your understanding of the world.

Matteo Tosato
Matteo Tosato

Responses (1)

Write a response