在基类构造函数中设置时,为什么重写的只用属性保持为null?

apf*_*l24 45 c# automatic-properties

我尝试了以下示例:

public class TestBase
{
    public virtual string ReadOnly { get; }

    public TestBase()
    {
        ReadOnly = "from base";
    }
}

class Test : TestBase
{
    public override string ReadOnly { get; }
    public Test()
    {
        // nothing here
    }
}
Run Code Online (Sandbox Code Playgroud)

当我创建一个Test实例时,我看到ReadOnly保持为null.但为什么?我真的不明白它,有人可以向我解释为什么会这样吗?至少我会期望和错误,只能在拥有类之外设置只读属性.

Mar*_*ell 39

编译器将其视为如下; 基本上,构造函数中的代码写入原始支持字段TestBase.看来你的不支持场景,但是......我确实想知道语言团队是否考虑过这种情况.

顺便说一句:如果你想看看编译器对代码的作用:sharplab.io

public class TestBase
{
    [CompilerGenerated]
    private readonly string <ReadOnly>k__BackingField; // note: not legal in "real" C#

    public virtual string ReadOnly
    {
        [CompilerGenerated]
        get
        {
            return <ReadOnly>k__BackingField; // the one in TestBase
        }
    }

    public TestBase()
    {
        <ReadOnly>k__BackingField = "from base";
    }
}
internal class Test : TestBase
{
    [CompilerGenerated]
    private readonly string <ReadOnly>k__BackingField;

    public override string ReadOnly
    {
        [CompilerGenerated]
        get
        {
            return <ReadOnly>k__BackingField; // the one in Test
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 除了可能添加警告之外,没有理智的方法可以解决这个问题.只是尝试在基础构造函数中添加一个`Console.WriteLine(ReadOnly);`,在分配给`ReadOnly`后你会看到一个不同的症状,它会调用我们知道的属性的后代getter.已分配,因此尝试打印"null".这里唯一合理的修复,如果有的话,编译后代类应该警告你实际上有两个不同的属性*实现*. (3认同)
  • 在任何人抱怨"谁在乎,你不应该这样做,因为它什么都不给你",然后考虑你可以在后代的重写属性中添加属性,这将与属性中的属性相结合.基类.所以*它*会给你一些东西,虽然它可能不是很多用途的功能,但它在序列化场景中可能很重要. (2认同)

Mat*_*son 17

解释这个的最简单方法是考虑编译器生成什么代码来实现它.

基类等同于:

public class TestBase
{
    public virtual string ReadOnly => _testBaseReadOnly;

    public TestBase()
    {
        _testBaseReadOnly = "from base";
    }

    readonly string _testBaseReadOnly;
}
Run Code Online (Sandbox Code Playgroud)

派生类等同于:

class Test : TestBase
{
    public override string ReadOnly => _testReadOnly;

    readonly string _testReadOnly;
}
Run Code Online (Sandbox Code Playgroud)

这里要注意的重要一点是派生类有它的OWN BACKING FIELD ReadOnly- 它不会重用基类中的那个.

意识到这一点后,显然为什么被覆盖的属性为null.

这是因为派生类有自己的支持字段ReadOnly,并且其构造函数不初始化该支持字段.

顺便说一句,如果你正在使用Resharper它,它实际上会警告你,你没有ReadOnly在派生类中进行设置:

 "Get-only auto-property 'ReadOnly' is never assigned."
Run Code Online (Sandbox Code Playgroud)