在某些使用了 readonly 关键字的情况下,C#编译器会创建出结构体的防御副本。虽然这个问题已经众所周知并被记录下来了,但仍然值得重新审视,因为它与C# 7.2 的几个特性有关。in 和ref readonly 关键字的使用让这个问题出现得更频繁,而readonly 结构体提供了一种解决方法。
C#中的结构体通常用于提升性能,减少用于分配和销毁内存的开销。然而,潜在的陷阱限制了它们的使用。C# 7.2 增加了一个改进的 readonly 结构体来解决这个问题。
在如下几种情况下,C#编译器将为结构体创建副本:
-
结构体不是只读的。
-
机构提变量使用了 readonly 修饰符。
-
调用方法(包括属性)。
public struct SomeStruct { private int _x; public int X { get { return _x; } } } private readonly SomeStruct s = new SomeStruct(42); s.X; // 编译器创建了一个防御副本。
当 x 是 in 参数、ref readonly 局部变量或通过 readonly reference 返回值的方法调用的结果时,适用相同的规则。
public void BadFunction(in SomeStruct s) { s.X; // 编译器创建了一个防御副本。 }
C# 7.2 增加了声明 readonly 结构体的可能性,并提供了避免创建防御副本的解决方案。声明为 readonly 的结构体不能具有属性设置器,并且不能对结构体成员赋值。
我们可以通过静态分析来检测防御性副本问题。 ErrorProne.NET 的灵感来自于 ErrorProne,一个 Java 静态分析工具。移植到.NET 的版本由一组 Roslyn 分析器组成,侧重于准确性和性能。其中有一部分分析器适用于结构体,以Nuget 包的方式供下载使用。
查看英文原文: Performance of Structs in C# 7.2
评论