为什么在C# 6.0自动属性初始化中`this`不可用?

23

我有以下的代码类:

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

    public class Nested
    {
        public Nested(Foo foo)
        {
            foo.DoSomething();
        }
    }

    private void DoSomething()
    {

    }
}

然而,我遇到了这个编译错误:

关键字“this”在当前上下文中不可用

我可以通过简单地不使用自动属性初始化器,而是显式地将其移入构造函数来解决它:

public Nested Bar { get; }

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

为什么会这样?难道自动属性初始化器不是实际上被翻译成IL中的构造函数代码吗?


3
因为自动属性初始化器被翻译成带有初始化器的后备字段,所以您不能在字段初始化器中引用它。因为字段初始化器在构造函数之前运行,所以还没有构造出对象可供使用。 - Evk
@Evk 不是这样吗?当构造函数运行时,对象也是“尚未构建”的吗? - Roland Pihlakas
一个有趣的问题Roland Pihlakas!这个对象必须已经存在于内存中,准备好访问。否则该属性将不存在。但是没有其他初始化。构造函数中也可能会执行错误的初始化步骤,但对程序员来说哪种情况更明显呢? - Droidum
否则会发生这种情况。 - user253751
2个回答

41

简单来说:你不能在初始化程序中使用 this。这样做的想法是防止不完整的对象逃逸-Nested(this) 可以对你的对象执行任何操作,导致非常混乱和难以理解的错误。请记住,初始化程序在您添加的任何构造函数之前执行。同样的事情也会在字段初始化程序中失败,方式完全相同:

private Nested _field = new Nested(this);

本质上来说,初始化器旨在执行简单的初始化操作 - 解决98%的问题。任何涉及this的东西都更加复杂,您将需要编写自己的构造函数 - 并为任何时间问题承担责任 :)


今天我学到了!如果你不介意的话,我有点好奇:是否有关于初始化程序中可以包含什么内容的详细文档?为什么他们不把它放在构造函数中(在显式构造函数代码之前)呢? - Luke Vo
4
这基本上是在IL级别发生的情况;我非常有意地说“你添加的任何构造函数”,因此:如果您添加了一个构造函数,它还没有时间执行任何操作。还有初始化顺序的问题:哪些初始化程序已经运行?如果没有构造函数的明确时间控制,正确性变得非常棘手。语言作者不喜欢增加微妙的复杂性,因此:必须使用构造函数来实现这种用法。 - Marc Gravell
1
@datvm 在构造函数之前这样做并不能防止像 private int a = this.b; private int b = this.a; 这样的情况 - 虽然特定的无法解决的情况可能会被捕获,但是禁止在该上下文中使用 this 更简单。 - IllusiveBrian

16

为什么会这样?难道自动属性初始化器不是被翻译成IL中的构造函数代码吗?

自动实现属性初始化器的规则与字段初始化器相同,原因也相同。请注意,属性初始化器在基类体之前执行,就像字段初始化器一样 - 因此您仍处于“有点未初始化”的对象上下文中;比构造函数体更多。

因此,您应该将属性转换为以下内容:

private readonly Nested bar = new Nested(this); // Invalid

public Nested Bar
{
    get { return bar; }
}

简而言之,这个限制是为了防止您陷入麻烦。如果您需要在初始化属性时引用this,只需按照第二个示例手动执行即可。(根据我的经验,这相对较少见。)


它不是被转换为“只读”字段了吗?当然,这并不重要,我们需要知道错误发生的原因。 - TheLethalCoder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接