C# – 为什么在字段初始值设定项中无法访问’this’?

为什么会产生编译器错误:

class Foo { public Bar Baz = new Bar(this); } 

但这不是:

 class Foo { public Bar Baz; public Foo() { this.Baz = new Bar(this); } } 

从概念上讲,这两者是等价的,不是吗?

不,它们不完全等效……变量初始化程序运行任何基类构造函数之前执行。 构造函数的主体在运行基类构造函数执行。 (这与Java不同,其中变量初始化程序在基类构造函数之后但在构造函数体之前执行。)

因此,在构造函数体内访问它更安全:您可以确保该对象至少在其基类(及其向上)方面被初始化。

无论如何,我相信这是推理……

因为在那一点(在你的第一个例子中),对象尚未构建。 在构造函数中它已经存在。

在构造函数体中,您可以选择进行额外的初始化,但只有在您希望的情况下 – 更改字段值,调用其他方法,等等。 但这是可选的。 因此,您可能依赖于技术构造的对象,而它可能并非根据您的应用程序逻辑构建。 但是,你必须注意,而不是编译器。

因为您引用的类尚未初始化。

访问尚未初始化的类是不合逻辑的。 如果您能够访问它,则意味着Bar已经初始化,直到构造函数未完成才真实。

 class Foo { public Bar Baz = new Bar(this); } 

“this”代表“Foo”类的对象。 此时您还没有启动类对象。

假设,“Bar”构造函数不需要“Foo”实例来启动(或调用其构造函数),如Bar(“hello!”),那么上面的语句可以编译为:

 class Foo { public static Bar Baz = new Bar("Hello!"); } 

可通过以下方式访问:

 Foo.Baz.something(); 

类可以启动一个应该用“static”关键字标记的实例,并且不需要启动类实例。

其他一点没有提到的答案是C#和vb.net中的字段初始化规则不同。 在C#中,如果Foo:Bar:Boz:Object,构造函数和字段初始值设定项按顺序运行:

  1. Foo初始化器
  2. 条形初始化器
  3. Boz初始化器
  4. Boz构造函数
  5. 酒吧构造函数
  6. Foo构造函数

在vb.net中,序列是:

  1. Boz的初始化者
  2. Boz构造函数
  3. 条形初始化器
  4. 酒吧构造函数
  5. Foo初始化器
  6. Foo构造函数

vb.net方法的一个优点是字段初始化器可以使用正在构造的对象,因为对象及其基类型中的所有内容都已初始化到初始化点。 在某些情况下,C#方法可能会有所帮助,但考虑到所谓的优势只适用于虚拟方法所需的字段可以在没有直接初始化的情况下应用,我认为它们相对较少。或间接需要任何构造函数的参数; 这些情况几乎总是可以通过让虚方法检查对象是否已初始化来处理,如果没有则初始化它(它将具有“早期”字段初始化器可用的所有信息)。

坦率地说,如果有一个合理的方法让字段初始化程序访问构造函数参数,我认为vb.net和c#都会得到改进。 在vb中,由于字段初始化程序在构造基类之后运行,因此可以使用基类构造函数将其参数存储到字段中,并使派生类构造函数使用该字段初始化其他字段。 例如,如果MyArray[]在对象的整个生命周期中始终引用相同的数组,则说:

 'ConstructorParam在基类ParamInitClass(Of Integer)中定义和设置
   ReadOnly MyArray(ConstructorParam)As Integer
 ...
 Sub New(ArraySize As Integer)
   MyBase.New(ARRAYSIZE)
结束子

似乎更清楚

   ReadOnly MyArray()As Integer

 Sub New(ArraySize As Integer)
   Redim MyArray(ArraySize)
结束子

请注意,在C#中,可以尝试执行此类操作的唯一方法是通过threadstatic变量,但这样的技术threadstatic只是狡猾。