我想我已经听过一个术语" ref like struct"在GitHub前一段时间。
现在我掌握了最新的C#版本(7.3),我终于可以自己测试了。所以这似乎是一个有效的代码:
public ref struct MyStruct
{
int x;
}
我知道什么是ref locals和ref返回,因为有关于它的文档。但是我找不到关于ref struct的文档。
参考结构不能用于自动属性或字段。它们也不能被投射到对象上。这些是实证研究结果。
使用" Span"新c#最近给我的背景我猜想ref struct只是一个堆栈结构。这是一个永远不会堆的结构。但我不是百分百肯定。
我很确定应该有关于此的文档,但我没有找到它。
答案 0 :(得分:6)
经过一番研究,我在Compile time enforcement of safety for ref-like types in C# 7.2上偶然发现了这篇文章。
此C#功能也称为“内部指针”或“类似于ref的类型”。该提议允许编译器要求某些类型(如
Span<T>
)仅出现在堆栈中。
该网站还说明了这样做的好处,主要涉及垃圾收集和堆栈分配。
使用类似ref的类型也会带来一些限制,例如:
这限制了它们用于参数,局部变量,在某些情况下还可以返回值。
还有一个official documentation from Microsoft,正如@UnholySheep在评论中指出的那样。
答案 1 :(得分:4)
只需在另一个答案中添加一点。基本上,他们创建了一个ref结构,以便能够将托管指针作为成员保存。这意味着它不能被垃圾收集,如果它最终堆在堆上,GC就会崩溃。对你能做什么和不做什么的奇怪限制都与此有关(如微软文档中所述):
Microsoft docs on reference semantics in C# 7.2
所有这一切都非常吸引人,但并没有真正解释为什么他们提供了这种功能。真正的原因是允许处理托管和非托管内存的api具有公共接口(即不需要无限重载)。
本博客详细解释了这一点:
答案 2 :(得分:4)
从 C#7.2 中添加的内容并不是真正意义上的 功能 ,而是以如此标记的值添加或启用任何新功能类型本身,而是声明或发布特定的 限制 ,该规则控制在其他任何地方对该类型的允许使用。
[编辑:在github / dotnet站点上参见span-safety]
因此,与其考虑使用什么ref struct
名称 ,还不考虑考虑它得到了什么 。从逻辑上说,对外部使用施加任何限制都需要ref struct
这样假定的相关保证,因此,关键字的作用是授权ref struct
或“许可证” 做事需要这些特定的保证。
重点在于,这是间接的好处,因为通常被ref struct
许可使用的操作类型基本上与关键字无关,可以实现和尝试,不论是否标记ref struct
,都可以通过任意位置的巧妙编码甚至成功实现。
理论部分太多了。实际上,什么是“活泼的代码” 用例,以这种方式依赖于附加保证,甚至达到接受所有附带限制的极端程度?本质上,struct
能够公开对其自身或其中一个字段的托管引用。
通常, C#对this
引用泄漏出struct
的任何实例方法实施严格的限制:
error CS8170: Struct members cannot return 'this' or other instance members by reference
编译器必须确定this
几乎不可能泄漏出值类型,因为有可能(在某些用途中,很可能)临时将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
消失(我认为这是一个错误,请参阅here和here)。>
由于编译器仍然无法阻止应该正确允许ref struct
返回自己的this
的代码,因此,您必须求助于一些蛮横的技术来避免致命错误( s)在假定解放的ref struct
实例方法中进行编码。也就是说,用 C#编写的值类型实例方法合法地需要覆盖致命错误CS8170
// CS8157
,可以使“ this”指针不透明,将其通过IntPtr
往返。这留给读者练习,但是一种实现方法是通过System.Runtime.CompilerServices.Unsafe包。