const,只读和可变值类型
我正在继续研究C#和语言规范,这里有另外一个我不太明白的行为:
C#语言规范在第10.4节中明确说明了以下内容:
常量声明中指定的类型必须是sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal,bool,string,枚举类型或引用类型。
它还在第4.1.4节中陈述了以下内容:
通过常量声明,可以声明简单类型的常量(第10.4节)。 不可能有其他结构类型的常量,但类似的效果由静态只读字段提供。
好的,所以通过使用静态只读可以获得类似的效果。 读这个,我去了,并尝试以下代码:
static void Main()
{
OffsetPoints();
Console.Write("Hit a key to exit...");
Console.ReadKey();
}
static Point staticPoint = new Point(0, 0);
static readonly Point staticReadOnlyPoint = new Point(0, 0);
public static void OffsetPoints()
{
PrintOutPoints();
staticPoint.Offset(1, 1);
staticReadOnlyPoint.Offset(1, 1);
Console.WriteLine("Offsetting...");
Console.WriteLine();
PrintOutPoints();
}
static void PrintOutPoints()
{
Console.WriteLine("Static Point: X={0};Y={1}", staticPoint.X, staticPoint.Y);
Console.WriteLine("Static readonly Point: X={0};Y={1}", staticReadOnlyPoint.X, staticReadOnlyPoint.Y);
Console.WriteLine();
}
这段代码的输出是:
静态点:X = 0; Y = 0
静态只读点:X = 0; Y = 0
抵销...
静态点:X = 1; Y = 1
静态只读点:X = 0; Y = 0
按一下键退出...
我真的希望编译器给我一些关于变异静态只读字段或失败的警告,以便像引用类型那样对字段进行变异。
我知道可变值类型是邪恶的(为什么微软曾经将Point
实现为mutable是一个谜),但是编译器不应该以某种方式警告你试图改变静态只读值类型吗? 或者至少警告你,你的Offset()
方法不会有“期望的”副作用?
Eric Lippert解释了这里发生的事情:
...如果该字段是只读的,并且引用发生在声明该字段的类的实例构造函数之外,那么结果是一个值,即由E引用的对象中的字段I的值。
这里重要的一点是结果是字段的值,而不是与字段相关的变量。 只读字段不是构造函数之外的变量。 (这里的初始化程序被认为是在构造函数内部;请参阅我之前关于该主题的文章。)
哦,只是要强调可变结构的邪恶,这里是他的结论:
这是可变值类型是邪恶的另一个原因。 尽量使价值类型不变。
只读的一点是,您不能重新分配参考或值。
换句话说,如果你试图这样做
staticReadOnlyPoint = new Point(1, 1);
你会得到一个编译器错误,因为你试图重新分配staticReadOnlyPoint
。 编译器会阻止你这样做。
然而, readonly
不强制值或引用对象本身是否可变 - 这是由创建它的人员在类或结构中设计的行为。
[ 编辑 :妥善解决被描述的奇怪行为]
您看到staticReadOnlyPoint
看起来是不可变的行为的原因不是因为它本身是不可变的,而是因为它是只读结构。 这意味着每次访问它时,都会收到完整的副本。
所以你的线
staticReadOnlyPoint.Offset(1, 1);
正在访问和变异字段的副本,而不是字段中的实际值。 当你随后写出你的值,然后写出原件的另一个副本(而不是变异的副本)。
您调用Offset
所做的副本将被丢弃,因为它永远不会分配给任何东西。
编译器根本没有足够的关于方法的信息来知道该方法会改变结构。 一个方法可能有一个有用的副作用,但不会改变结构的任何成员。 如果技术上可以将这种分析添加到编译器中。 但是这对于居住在另一个装配中的任何类型不起作用。
缺少的成分是一个元数据标记,指示方法不会改变任何成员。 像C ++中的const关键字一样。 无法使用。 如果它被添加到原始设计中,它将会非常不符合CLS标准。 有很少的语言支持这个概念。 我只能想到C ++,但我并没有太多。
Fwiw,编译器确实生成了明确的代码,以确保语句不会意外地修改只读。 这个说法
staticReadOnlyPoint.Offset(1, 1);
被翻译成
Point temp = staticReadOnlyPoint; // makes a copy
temp.Offset(1, 1);
添加代码然后比较该值并生成运行时错误也仅在技术上可行。 它成本太高。
链接地址: http://www.djcxy.com/p/21077.html上一篇: const, readonly and mutable value types
下一篇: Why do I NOT get warnings about uninitialized readonly fields?