C# 为什么在基类构造函数中设置时,重写的get only属性保持null?
我尝试了以下示例:C# 为什么在基类构造函数中设置时,重写的get only属性保持null?,c#,automatic-properties,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() { /
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
}
}
当我创建一个Test实例时,我看到ReadOnly保持为null。但是为什么呢?
我真的不懂,谁能给我解释一下为什么会这样?至少我希望有一个错误,即只读属性不能设置在拥有的类之外。 < P>最简单的解释方法是考虑编译器生成的代码来实现这个。 基类与此等效:
public class TestBase
{
public virtual string ReadOnly => _testBaseReadOnly;
public TestBase()
{
_testBaseReadOnly = "from base";
}
readonly string _testBaseReadOnly;
}
class Test : TestBase
{
public override string ReadOnly => _testReadOnly;
readonly string _testReadOnly;
}
派生类与此等效:
public class TestBase
{
public virtual string ReadOnly => _testBaseReadOnly;
public TestBase()
{
_testBaseReadOnly = "from base";
}
readonly string _testBaseReadOnly;
}
class Test : TestBase
{
public override string ReadOnly => _testReadOnly;
readonly string _testReadOnly;
}
这里需要注意的重要一点是,派生类有自己的支持字段,用于ReadOnly
——它不会重复使用基类中的支持字段
意识到这一点后,应该很清楚为什么重写的属性为null
这是因为派生类有自己的ReadOnly
支持字段,并且它的构造函数没有初始化该支持字段
顺便说一句,如果您使用的是Resharper
,它实际上会警告您没有在派生类中设置ReadOnly
:
"Get-only auto-property 'ReadOnly' is never assigned."
编译器将其处理如下;基本上,构造函数中的代码写入原始备份字段,位于
TestBase
。似乎您的场景不受支持,但是。。。我不知道语言小组是否考虑过这件事
顺便说一句:如果你想看看编译器对代码做了什么:
公共类测试库
{
[编译生成]
私有只读字符串k__BackingField;//注意:在“real”C中不合法#
公共虚拟字符串只读
{
[编译生成]
得到
{
返回k__BackingField;//TestBase中的那个
}
}
公共测试库()
{
k_uubackingfield=“自基”;
}
}
内部类测试:TestBase
{
[编译生成]
私有只读字符串k__BackingField;
公共重写字符串只读
{
[编译生成]
得到
{
返回k_ubackingfield;//测试中的那个
}
}
}
@SeM这不是一个真正的重复,因为这个问题是关于重写字符串属性的影响的。@MatthewWatson你说的是第一个还是第二个?@SeM两者都有,因为这两个答案都没有提到重写属性。我想这和构造函数中的虚拟成员访问有关,不是吗?不过,如果你看看它是如何编译的,这一点很清楚:两个类都有自己的私有支持字段。返回子类中的一个。对于奖励积分,添加Console.WriteLine(只读)在基础构造函数中,在赋值给属性之后,在任何人抱怨“谁在乎,你不应该这样做,因为它不给你任何东西”,然后考虑你可以将属性添加到子代中的重写属性中,这将与基类中属性的属性相结合。所以它确实给了你一些东西,虽然它可能不是很多人使用的功能,但它在序列化场景中可能很重要。@LasseVågsætherKarlsen我真的很想发送“Jared信号”——有点像蝙蝠信号,但是。。。怪人。我应该这样做吗?当然,我个人认为在构造函数中使用虚拟成员是错误的,但是编译器没有明确地警告它,而且事实上确实可以工作,所有关于什么可能中断以及为什么中断的常见文档警告,所以在这里我确实希望属性被分配,尽管我完全理解为什么不这样做。也许这个特定的场景需要明确地处理,无论是警告还是实际的支持。虽然我觉得为这个添加完全的支持闻起来像一罐蠕虫,但是的,我相信知识和意志决定,所以点亮泛光灯,让团队决定:)没有明智的方法来解决这个问题,除了可能添加警告之外。只需添加一个Console.WriteLine(只读)在基本构造函数中,分配给ReadOnly
后,您将看到不同的症状,它将调用我们知道尚未分配的属性的后代getter,并因此尝试打印null
。这里唯一合理的修复方法是编译子类时应该警告您实际上有两个不同的属性实现。