While browsing through the source code of the .NET framework(what else would you do with some free time?) I noticed the usage of the scoped
keyword:
/// <summary>Writes the specified character span to the handler.</summary> | |
/// <param name="value">The span to write.</param> | |
/// Notice the scoped keyword 👇 here | |
public void AppendFormatted(scoped ReadOnlySpan<char> value) | |
{ | |
// Fast path for when the value fits in the current buffer | |
if (value.TryCopyTo(_chars.Slice(_pos))) | |
{ | |
_pos += value.Length; | |
} | |
else | |
{ | |
GrowThenCopySpan(value); | |
} | |
} |
This triggered my interest as I didn't know that this keyword existed. Time to find out what it does...
To explain what this keyword does, I have to talk first about ref struct
. C# 8 introduced ref struct
where it was used to implement the Span
type. A ref struct
guarantees that an instance of the type is allocated on the stack and can’t escape to the managed heap. To guarantee this, strict scoping rules are enforced by the compiler.
More about ref structure types here: ref struct types - C# reference | Microsoft Learn
When you pass references to methods on a ref struct
, the compiler ensures that the variables you refer to don't go out of scope before the struct
itself. Otherwise, the ref struct
might refer to out-of-scope variables:
public ref struct ConversionRequest | |
{ | |
public ConversionRequest(double rate) | |
{ | |
Rate = rate; | |
} | |
public double Rate { get; } | |
public void Convert(Span<double> values) | |
{ | |
} | |
} |
public class Converter | |
{ | |
public void Handle(ref ConversionRequest request) | |
{ | |
Span<double> values = stackalloc double[10]; | |
//Will not compile! | |
request.Convert(values); | |
} | |
} |
The code above results in a compiler error:
The fix is to introduce the scoped
keyword. Now the compiler will allow to pass these variables and it will treat the argument with the same scope as a local variable in the method.
public ref struct ConversionRequest | |
{ | |
public ConversionRequest(double rate) | |
{ | |
Rate = rate; | |
} | |
public double Rate { get; } | |
public void Convert(scoped Span<double> values) | |
{ | |
} | |
} |
Hope that helps…