定义网站

时间:2018-01-12 22:24:40

标签: c# struct ref c#-7.0

我想我已经听过一个术语" ref like struct"在GitHub前一段时间。

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

public ref struct MyStruct
{
    int x;
}

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

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

使用" Span"新c#最近给我的背景我猜想ref struct只是一个堆栈结构。这是一个永远不会堆的结构。但我不是百分百肯定。

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

3 个答案:

答案 0 :(得分:6)

经过一番研究,我在Compile time enforcement of safety for ref-like types in C# 7.2上偶然发现了这篇文章。

  

此C#功能也称为“内部指针”或“类似于ref的类型”。该提议允许编译器要求某些类型(如Span<T>)仅出现在堆栈中。

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

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

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

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

还有一个official documentation from Microsoft,正如@UnholySheep在评论中指出的那样。

答案 1 :(得分:4)

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

Microsoft docs on reference semantics in C# 7.2

所有这一切都非常吸引人,但并没有真正解释为什么他们提供了这种功能。真正的原因是允许处理托管和非托管内存的api具有公共接口(即不需要无限重载)。

本博客详细解释了这一点:

Adam Sitnik on Span<T>

答案 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消失(我认为这是一个错误,请参阅herehere)。

由于编译器仍然无法阻止应该正确允许ref struct返回自己的this的代码,因此,您必须求助于一些蛮横的技术来避免致命错误( s)在假定解放的ref struct实例方法中进行编码。也就是说,用 C#编写的值类型实例方法合法地需要覆盖致命错误CS8170 // CS8157,可以使“ this”指针不透明,将其通过IntPtr往返。这留给读者练习,但是一种实现方法是通过System.​Runtime.​CompilerServices.​Unsafe包。