为啥 ref 结构不能用作类型参数?

Posted

技术标签:

【中文标题】为啥 ref 结构不能用作类型参数?【英文标题】:Why ref structs cannot be used as type arguments?为什么 ref 结构不能用作类型参数? 【发布时间】:2018-11-25 00:53:16 【问题描述】:

C# 7.2 introducedref structs。但是,给定一个像这样的ref struct

public ref struct Foo 
  public int Bar;

我不能将它用作类型参数:

int i = 0;
var x = Unsafe.As<int, Foo>(ref i); // <- Error CS0306 The type 'Foo' may not be used as a type argument.

我知道 ref 结构只能存在于堆栈上,而不能存在于堆上。但是,如果保证使用此类 ref 结构的泛型方法永远不会将它们放在堆上,就像上面使用 System.Runtime.CompilerServices.Unsafe 包的示例一样,该怎么办?为什么我不能在这些情况下将它们用作类型参数?

【问题讨论】:

泛型方法在另一个程序集中。它可能会做很多你不能用 ref structs 做的事情。在整个程序分析之外,编译器无法验证您所说的方法是否得到保证。我猜这样的分析被认为是禁止的,因为它不能安全地完成,所以它是不允许的。 【参考方案1】:

ref struct 做出的主要保证是它永远不会逃到堆中。

在泛型方法中,编译器不会验证无堆保证(因为几乎所有类型都可以存在于堆上)。防止泛型方法泄漏ref structs 的最直接方法是简单地禁止使用ref struct 作为类型参数,这就是C# 所做的。

从 C# 7.2 开始,您可以在声明中使用 ref 修饰符 属于结构类型。 ref struct 类型的实例分配在 堆栈并且无法逃逸到托管堆。为确保, 编译器限制 ref struct 类型的使用如下:

ref struct 不能是数组的元素类型。 引用结构不能是类字段的声明类型或非引用结构。 ref struct 不能实现接口。 不能将 ref 结构装箱为 System.ValueType 或 System.Object。 引用结构不能是类型参数。 lambda 表达式或本地函数无法捕获 ref struct 变量。 不能在异步方法中使用 ref struct 变量。但是,您可以在同步方法中使用 ref struct 变量,例如, 在那些返回 Task 或 Task 的地方。 不能在迭代器中使用 ref struct 变量。

More details from Microsoft about ref structs

【讨论】:

以上是关于为啥 ref 结构不能用作类型参数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥迭代器方法不能采用“ref”或“out”参数?

为啥我们不能使用 CloudFormation 中的参数将 AllowedValues 用作字符串?

为啥我不能更改实例化后用作属性的结构中的字段?

为啥我在设置中收到“'Bars' 不能用作实体类型 'Foo' 的属性”?

为啥不能将 WideString 用作互操作的函数返回值?

为啥我不能强制解开我的 $string 以用作 TextField 值?