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