

In version 7.2 of C# quite a few “small” features have been released to the public.

We so far have talked only about one of them in Let’s talk about Readonly Structs.

Today we are going to talk about a couple more. One of them concerning structs again in the form of ref struct and then we will also discuss about Span, a new type introduced in C# 7.2

Let’s begin.

With C# 7.2 we now have the ability to do the following.

ref struct CustomRefStruct { }

What this does is create a struct that can only live on the stack.

Now one would think that, given the fact that structs are value types, they would always live on the stack. Correct? Well, not necessarily.

A normal struct can be allocated on the heap on some specific occasions.

One of them is boxing, like in the following code sample

struct CustomStruct { } static void Main(string[] args) { var s = new CustomStruct(); Object boxed = s; }

This would box the struct into an object type which would be allocated to the heap.

This is a fact for all sorts of boxing value types into object types and why we generally wish to avoid boxing unless absolutely necessary. It is both time consuming and memory consuming. But boxing is a discussion for another day. Let’s move on.

Another way in which a struct may be allocated on the stack is when the struct is living inside an instance of a class.

For example

struct CustomStruct { } class CustomClass { public int IntValue { get; set; } public CustomStruct StructValue { get; set; } }

Now, whenever we instantiate a member of CustomClass the memory it will take in the heap will include room for the int value and the struct value. This again is valid for all value types and not only structs. As value types they do generally live on the stack, but, when part of a reference type they live on the heap.

So, conclusion, plain structs can be allocated in some cases on the heap, but ref structs (our subject of the day) cannot ever be part of the heap.

So, what does that mean in practice?

In practice it means that ref structs enforce some limitations when using those specific structs.

For one, the example above cannot be valid with a ref struct, i.e. it cannot be part of a class, whether it be static or instance member.

The following code snippet

ref struct CustomRefStruct { } class CustomClass { public int IntValue { get; set; } public CustomRefStruct StructValue { get; set; } }

would not compile as the compiler would throw an error.

Another limitation is that a ref struct cannot be a method parameter of an async method or a lambda.

The reason for this is that the compiler creates a class from lambda and async methods, and with that we would again place the struct on the heap, which is invalid.

So the following code snippet

ref struct CustomRefStruct { } public async Task MethodAsync(CustomRefStruct structValue) { }

again would not compile.

Also, obviously, we cannot do anything that would force our ref struct to be boxed into a reference type like the following.

ref struct CustomStruct { } static void Main(string[] args) { var s = new CustomRefStruct(); Object boxed = s; }

Ok, so what can we do with a ref struct?

Well it can be a

Method Parameter

A return type of a method

A local variable

And they more or less all mamke sense in our scenarios and based on our restrictions, as in all three cases, the struct would be allocated on the stack.

But, what is the point of this limited version of the struct? Why was it created?

Well, the reason behind this is Span

A Span is basically a representation of continuous arbitrary memory. In C# we have managed, unmanaged and stack allocated memory and we can wrap all 3 types of memory into a span and access it safely.

Now this is not the full analysis of Span and we will delve in deeper on a later blog post, but for now, this is a general description of what it is.

Now, why do we need it?

Well, basically we need a struct for it, as the Span contains two fiels

A pointer to the data itself

The length of the wrapped memory

Now, regardless of implementation, writes to a struct like this would not be atomic, thus, if it would live on the heap, we would always have to lock in order to guarantee atomicity. But applying a lock every time we would access the Span we would lose all the performance benefits it would provide us.

On the other hand we would skip the lock, but we would risk out of range access and type safety.

The second reason for needing ref struct for Span is that in CoreCLR the span contains a managed pointer in one of its fields, and those managed pointers cannot be fields of a heap object.

Now, the fact that Span is implemented by using ref struct internally, means that all limitations that apply to ref struct also apply to Span.

Conclusion

We have a new type called ref struct.

This new type, can only live on the stack. When should we use it?

We should use it when we wish to create a struct and enforce its usage only on the stack in order to reduce pressure from GC but also if if we have any other reason for not allowing heap memory allocation for our struct.

On of those reasons would be, if our struct would encapsulate other ref structs internally.

For example the following

ref struct CustomRefStruct { Span SpanOfInts; Span SpanOfDoubles; }

And one last thing. As always, ref struct is a new shiny feature of C#, know when to use it, but also know when not to use it as to not abuse it.