I think I've heard a term "ref like struct" in GitHub some time ago.
Now that I have my hands on latest C# version (7.3), I could finally test it my self. So this seems to be a valid code:
public ref struct MyStruct
{
int x;
}
I know what are ref locals and ref returns as there is documentation about that. But I could not find documentation about ref struct.
Ref structs can not be used on auto properties or fields. They can not be cast to object either. These were empirical findings.
With "Span" background that new c# gave me recently I guessed that ref struct is a stack only struct. That is an struct that never goes on heap. But im not 100% sure.
Im pretty sure there should be a documentation about this but I failed to find it.
What is a ref struct? Well, a ref struct is basically a struct that can only live on the stack. Now a common misconception is that since classes are reference types, those live on the heap and structs are value types and those live on the stack.
Structs are value types, while classes are reference types, and the runtime deals with the two in different ways. When a value-type instance is created, a single space in memory is allocated to store the value.
Yes they can. It depends. Many hold the stance that a struct should be immutable, and in this case, holding a reference to an object could mean it isn't.
The ref keyword indicates that a value is passed by reference. It is used in four different contexts: In a method signature and in a method call, to pass an argument to a method by reference. For more information, see Passing an argument by reference. In a method signature, to return a value to the caller by reference.
This addition to C# 7.2 is not really a feature in the sense of adding or enabling any new capability in the so-marked value-type itself, Rather, it allows the developer to declare or publish a specific restriction which governs the allowable use of that type everywhere else.
[edit: see span-safety at the github/dotnet site]
So instead of considering what ref struct
designation gives the end-user of the struct, consider how it benefits the author. Adding any restriction on external use logically entails a related guarantee that the ref struct
thus assumes, so the effect of the keyword is to empower or "license" the ref struct
to do things that require those specific guarantees.
The point is that it's an indirect benefit, because the kinds of operations that are typically considered licensed by ref struct
are basically none of the keyword's concern, and could be implemented and attempted, perhaps successfully even, by wily code anywhere, regardless of the ref struct
marking (or not).
So much for the theoretical part. In reality, what is the "wily code" use-case that so existentially depends on the additional guarantees, even to the extreme point of accepting all the accompanying limitations? Essentially, it's the ability for a struct
to expose a managed reference to itself or one of its fields.
Normally, C# enforces strong restrictions against the this
reference leaking out of any instance method of a struct
:
error CS8170: Struct members cannot return 'this' or other instance members by reference
The compiler has to be certain that there's virtually no possibility for this
to leak out of the value-type because it's possible (in some uses, quite likely) that the struct instance has been temporarily boxed for the purpose of calling the instance method, in which case there would be no persistent GetPinnableReference
instance relative to which a managed pointer to the struct
(or its interior) might be taken.
With all the ref
enhancements in recent years, C# now goes to even further lengths to detect and disallow this
from escaping. For example, in addition to the above, we now have:
error CS8157: Cannot return 'x' by reference because it was initialized to a value that cannot be returned by reference
..and related errors such as...error CS8374: Cannot ref-assign 'foo' to 'p' because 'foo' has a narrower escape scope than 'p'.
Sometimes the underlying reason for the compiler asserting CS8157
can be convoluted or hard to see, but the compiler hews to the conservative "better safe than sorry" approach, and this can sometimes result in false positives where, for example, you have additional special knowledge that the escape is ultimately contained within the stack.
For cases where CS8157
is genuinely unwarranted (i.e., in consideration of information that the compiler wasn't able to infer), these represent the "'perhaps even successful' wily code" examples I alluded to earlier, and generally there's no easy workaround, especially not via ref struct
. That's because the convoluted false-positives often only arise in higher-level ref
-passing code scenarios that would never be able to adopt the extreme limitations that are enforced for ref struct
in the first place.
Instead, ref struct
is used for very simple value-types. By guaranteeing them that their this
reference will always be anchored in an upper stack frame--and thus crucially, will never be awash in the GC heap--such types thus gain the confidence to publish managed pointers to themselves or their interiors.
Remember, however, that I said ref struct
is agnostic about how, why, and what the relaxations it provides are used for. What I was specifically alluding to there is that unfortunately, using ref struct
does not make CS8157
go away (I consider this a bug, see here and here).
Because ref struct
code that should properly be allowed to return its own this
is still prevented by the compiler from doing so, you have to resort to some rather brutish techniques to get around the fatal error(s) when coding within the supposedly liberated ref struct
instance methods. To wit, value-type instance methods written in C# that legitimately need to override fatal errors CS8170
/CS8157
can opaque the 'this' pointer by round-tripping it through an IntPtr
. This is left as an exercise for the reader, but one way to do this would be via the System.Runtime.CompilerServices.Unsafe package.
After some research, I stumbled upon this article on Compile time enforcement of safety for ref-like types in C# 7.2.
This C# feature is also known as “interior pointer” or “ref-like types”. The proposal is to allow the compiler to require that certain types such as
Span<T>
only appear on the stack.
The site also states the benefits of doing so, mainly concerning garbage collection and stack allocation.
Using ref-like types also brings some restrictions with it such as:
This limits them to be used for parameters, local variables, and in some cases return values.
There also exists an official documentation from Microsoft, as @UnholySheep pointed out in the comments.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With