C# 13 Ref Struct Interfaces
Intro
C# 从 7.2 开始引入了 ref struct
,ref struct
只能分配在栈上,不能被装箱,因为不能被装箱之前的版本中 ref struct
是不能实现接口的,在转成接口的时候会导致发生装箱,这是不被允许的,而我们在做一些设计的时候往往会使用到接口,用接口定义契约 contract,C# 13 开始我们可以允许 ref struct
实现接口,并且增加了可以作为泛型类型约束允许 ref struct
类型
Sample
来看一个简单的示例:
file interface IAge
{
public int GetAge();
}
file ref struct Age : IAge
{
public int GetAge() => 1;
}
使用的地方需要这样用:
private static void PrintAge<TAge>(TAge age)
where TAge : IAge, allows ref struct
{
Console.WriteLine(age.GetAge());
}
方法需要声明为泛型参数,并且要指定泛型约束 allows ref struct
我们不能直接声明参数为对应的接口类型,如 IAge
,这会导致 ref struct
被装箱,编译器会报错,例如我们这样定义,调用的时候编译器就会报错,这也意味着 ref struct
不能使用接口默认实现方法,因为接口默认实现方法需要转成接口才能使用,而转接口就意味着要发生装箱,对于 ref struct
来说是不被允许的
CS1503: Argument 1: cannot convert from 'CSharp13Samples.RefStructAge' to 'CSharp13Samples.IAge'
private static void PrintAge0(IAge age)
{
Console.WriteLine(age.GetAge());
}
另外需要显式声明 allows ref struct
默认是不允许将 ref struct
用作泛型类型参数的,不声明也会导致编译器报错
CS9244: The type 'RefStructAge' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'TAge' in the generic type or method 'RefStructInterfaceSample.PrintAge(TAge)'
除了方法我们也可以声明 Property
internal interface IAge
{
int AgeNum { get; }
int GetAge();
}
internal ref struct RefStructAge : IAge
{
public int AgeNum => 1;
public int GetAge() => AgeNum;
}
如果接口里有定义默认实现方法,在定义 ref struct
的时候就会报错
More
这一特性可以有助于避免 struct 被装箱导致性能问题,可以更好地利用 ref struct
带来的性能改进和编译器的约束检查
可以实现接口也有有助于在设计前面更加专注在更核心的接口设计上无需关心实现方式,实现的时候选择基于 class 实现也可以基于 ref struct
来实现, dotnet runtime 的一些 library 里的泛型参数也新增支持了这一特性,例如:https://github.com/dotnet/runtime/issues/102671
后续的 runtime library 应该也会基于这一特性优化一些原来基于 class 的实现,转而使用基于 ref struct 的实现来优化性能
对前面的示例做一个简单的 benchmark
References
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-72
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/ref-struct
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/structs#1623-ref-modifier
https://github.com/dotnet/csharplang/blob/main/proposals/csharp-13.0/ref-struct-interfaces.md
https://github.com/dotnet/csharplang/issues/7608
https://github.com/WeihanLi/SamplesInPractice/blob/main/net9sample/CSharp13Samples/RefStructInterfaceSample.cs
https://github.com/dotnet/runtime/issues/102671
https://github.com/dotnet/runtime/issues/105276