为什么我不能在初始化器中初始化只读变量?

为什么我不能在初始化器中初始化只读变量? 以下内容不起作用:

class Foo { public readonly int bar; } new Foo { bar=0; }; // does not work 

这是由于CLR的某些技术限制吗?

编辑

我知道new Foo { bar=0; } new Foo { bar=0; }new Foo().bar=0;相同new Foo().bar=0; ,但是CLR强制执行“readonly”,还是只是编译器限制?

允许在初始化程序中设置readonly引入在编译时无法执行的矛盾和复杂性 。 我想这个限制是为了避免含糊不清。 最关键的是编译时validation。

想象一下:

 class Foo { public readonly int bar; Foo () { // compiler can ensure that bar is set in an invoked ctor bar = 0; } } // compiler COULD know that `bar` was set in ctor // and therefore this is invalid new Foo { bar = 0; } 

现在,考虑一下:

 class Foo { public readonly int bar; Foo () { // imagine case where bar not set in ctor } } // compiler COULD know that `bar` is not bound yet // therefore, this COULD be valid new Foo { bar = 0; } // but this COULD be proved to never be valid new Foo(); 

想象一下,上述两种情况都是统一的(例如,“通过编译器魔术”),但是,请输入generics:

 T G () where T : new { // What in heck should happen *at compile time*? // (Consider both cases above.) // What happens if T (Foo) changes to include/not-include setting the // readonly variable in the ctor? // Consider intermediate code that invokes G() and this other // code is NOT recompiled even though Foo is-- // Yet a binary incompatibility has been added! // No thanks! return new T(); } G(); 

我相信我概述的案例显示了使用“动态” readonly方法的一些复杂性,并且在一天结束时,我认为它仅仅是一种选择的语言限制 (编译器实现语言)来强制/允许编译时validation。

初始化程序只是语法糖。 当你写:

 new Foo { bar=0; }; 

(顺便说一下,这是一个语法错误,应该是这个…)

 new Foo { bar=0 } 

实际发生的是:

 var x = new Foo(); x.bar = 0; 

由于该属性是只读的,因此该第二个语句无效。

编辑:根据您的编辑,问题有点不清楚。 根据设计, readonly属性是不可设置的。 它建立在对象构造上。 这由编译器和运行时强制执行。 (不可否认,我没有对后者进行测试,因为绕过前者需要一些技巧。)

请记住,有两个阶段的“编译”。 在将C#代码编译为IL代码时强制执行,并在将IL代码编译为机器代码时强制执行。

这不是CLR的技术限制,并且在给定显式readonly声明的情况下,它的工作正常。 构造对象后,您无法设置readonly属性。

由于readonly变量必须在构造函数中初始化,并且属性初始化程序在构造对象后执行,这是无效的。

因为初始化程序相当于

 var foo = new Foo(); foo.bar=0; 

这是幕后的重写。

你要做的是这样的:

  class Foo { public readonly int bar; Foo(int b) { bar = b; // readonly assignments only in constructor } } Foo x = new Foo(0); 

因为您指定它是只读的。 指定某些内容只是readonly然后期望write语句起作用是没有意义的。

根据此页面 ,CLR默认 – 在处理初始化程序列表之前首先构造对象,因此您将分配给bar两次(一次在默认构造时,一次在处理初始化程序时)。

这是readonly关键字的实现结果。 以下引用取自以上的MSDN参考 :

readonly关键字是一个可以在字段上使用的修饰符。 当字段声明包含只读修饰符时,声明引入的字段的赋值只能作为声明的一部分或在同一个类的构造函数中出现。

您的代码或假设没有太大的错误,例外情况可能是初始化程序列表的一个重要特征是不强加任何序列约束(对于C ++尤其如此)。 分号是一个排序运算符,因此初始化列表以逗号分隔。

除非你认为规范在定义上是正确的,否则我认为这里的语言规范是错误的。 它部分地打破了语言的一个重要特征,即readonly的概念。 其他答案中提到的歧义问题在我看来是一个单一的根本原因。 Readonly是一个非常具有侵入性的特性,对于const正确性的一半是很难正确的,更重要的是,它对开发的编码风格有害。

您正在寻找并可能在此期间找到的是命名参数: https : //stackoverflow.com/a/21273185/2712726这不是您要求但接近。

同样公平地说,我必须补充说,有很多知识渊博的作者完全不同意这些关于C ++开发人员经常拥有的const正确性的观点。 Eric Lippert无可否认地发表了这篇文章(对C ++开发人员感到恐惧)声明: https : //stackoverflow.com/a/3266579/2712726