什么是定义网站中的ref struct

M.k*_*ary 4 c# struct ref c#-7.0

我想我前段时间在GitHub中听过"ref like struct"这个词.

现在我掌握了最新的C#版本(7.3),我终于可以自己测试了.所以这似乎是一个有效的代码:

public ref struct MyStruct
{
    int x;
}
Run Code Online (Sandbox Code Playgroud)

我知道什么是ref本地和ref返回,因为有关于它的文档.但是我找不到关于ref struct的文档.


引用结构不能用于自动属性或字段.它们也不能被投射到对象上.这些都是实证研究结果.

有了新的c#最近给我的"Span"背景,我猜测ref struct是一个只有堆栈的结构.这是一个永远不会堆的结构.但我不是100%肯定.

我很确定应该有关于此的文档,但我没有找到它.

Ian*_* H. 8

经过一番研究后,我偶然发现了一篇关于在C#7.2中编译ref类似类型的安全性的文章.

此C#功能也称为"内部指针"或"类似于ref的类型".该提议允许编译器要求某些类型,例如Span<T>仅出现在堆栈上.

该网站还说明了这样做的好处,主要涉及垃圾收集和堆栈分配.


使用类似ref的类型也会带来一些限制,例如:

  • ref-like类型不能是数组元素的类型
  • ref-like类型不能用作泛型类型参数
  • ref-like变量不能装箱
  • ref-like类型不能是普通的非ref类型的字段
  • ref-like类型无法实现接口
  • 间接限制,例如在异步方法中不允许使用类似ref的类型,这实际上是禁止类似ref的类型字段的结果.

这限制了它们用于参数,局部变量以及在某些情况下返回值.


还有来自微软官方文档,正如@UnholySheep在评论中指出的那样.


Gle*_*den 6

这除了C#7.2是不是一个真正的功能,在添加或启用在这样标记的任何新功能的感值类型本身,相反,它允许开发人员声明或发布特定的限制支配的允许使用的那在其他地方输入。

[编辑:请参阅github / dotnet网站上的span-safety ]

因此ref struct,与其考虑赋予结构最终用户什么名称,不如考虑给作者带来什么好处。从逻辑上说,在外部使用上加任何限制都必然会带来一个相关的保证,ref struct因此,关键字的作用是授权或“许可”ref struct做需要这些特定保证的事情。

关键是,这是间接的好处,因为通常认为由其许可的操作种类ref struct基本上与关键字无关,并且可以通过任意位置的任意代码(无论ref struct标记(或不)。

理论部分就这么多。实际上,什么才是“ wily code” 用例,它如此存在地依赖于附加保证,甚至达到接受所有附带限制的极端?本质上,这是一种能力,struct可以将托管引用公开给自身或其字段之一。

通常,C#this从a的任何实例方法中泄漏的引用实施严格的限制struct

error CS8170: Struct members cannot return 'this' or other instance members by reference

编译器必须确定几乎没有可能this泄漏出值类型,因为有可能(在某些用途中,很有可能)为了调用instance方法的目的而将struct实例临时装箱了。在这种情况下,将不会有任何持久GetPinnableReference实例可以相对于该实例获取指向struct(或其内部)的托管指针。

通过ref近年来的所有增强功能,C#的工作更加深入,可以检测并禁止this转义。例如,除了上述内容,我们现在还有:

error CS8157: Cannot return 'x' by reference because it was initialized to a value that cannot be returned by reference

..以及相关错误,例如...

error CS8374: Cannot ref-assign 'foo' to 'p' because 'foo' has a narrower escape scope than 'p'.

有时,使编译器断言的根本原因CS8157可能令人费解或难以理解,但是编译器偏向于保守的“安全胜于遗憾”方法,这有时会导致误报,例如,您还具有其他一些特殊知识,那就是:转义符最终包含在堆栈中。

对于CS8157确实没有必要的情况(即,考虑到编译器无法推断的信息),它们代表了我之前提到的“可能甚至成功的巧妙代码”示例,通常没有简单的解决方法,特别是不通过ref struct。这是因为令人费解的假阳性经常仅出现在更高级别的ref传递代码的场景中,而这些场景永远无法采用ref struct最初强加的极端限制。

而是ref struct用于非常简单的值类型。通过保证它们的this引用将始终锚定在较高的堆栈框架中-因此至关重要的是,它们绝不会在GC堆中泛滥成灾-这样的类型因此有信心发布指向其自身或内部的托管指针。

但是请记住,我所说ref struct的关于它提供的放松方式,原因和用途是不可知的。我特别提到的是,不幸的是,使用ref struct并不会CS8157消失(我认为这是一个错误,请参阅此处此处)。

由于编译器仍然阻止ref struct应允许其返回自身代码的代码this这样做,因此在假定为自由的ref struct实例方法中进行编码时,您必须求助于一些相当残酷的技术来避免致命错误。也就是说,用C#编写的值类型实例方法合法地需要覆盖致命错误CS8170?/?。CS8157可以通过在指针上往返来使“ this”指针不透明IntPtr。留给读者练习,但这是通过System.Runtime.CompilerServices.Unsafe包进行的一种方法。


Ada*_*own 5

只是在另一个答案中添加一点。基本上,他们创建了一个 ref 结构,以便能够将托管指针作为成员保存。这意味着它不能被垃圾回收,如果它最终出现在堆上,GC 就会崩溃。关于您可以做什么和不能做什么的奇怪限制都与此有关(如此处的 microsoft 文档中所述):

有关 C# 7.2 中的引用语义的 Microsoft 文档

所有这些都非常迷人,但并没有真正解释为什么他们提供了这个功能。真正的原因是允许同时处理托管和非托管内存的 api 具有公共接口(即消除对无限重载的需要)。

这在此博客中有详细说明:

亚当·西特尼克关于 Span<T>

  • 这个名字真的是误导。为什么叫ref?它与通过引用传递结构有什么关系吗?如果不是,我们是否应该在概念上将其视为“仅堆栈”? (3认同)