作者:无声胜有剩 | 来源:互联网 | 2022-11-27 17:33
我尝试了以下示例:
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.但为什么?我真的不明白它,有人可以向我解释为什么会这样吗?至少我会期望和错误,只能在拥有类之外设置只读属性.
1> Marc Gravell..:
编译器将其视为如下; 基本上,构造函数中的代码写入原始支持字段TestBase
.看来你的不支持场景,但是......我确实想知道语言团队是否考虑过这种情况.
顺便说一句:如果你想看看编译器对代码的作用:sharplab.io
public class TestBase
{
[CompilerGenerated]
private readonly string k__BackingField; // note: not legal in "real" C#
public virtual string ReadOnly
{
[CompilerGenerated]
get
{
return k__BackingField; // the one in TestBase
}
}
public TestBase()
{
k__BackingField = "from base";
}
}
internal class Test : TestBase
{
[CompilerGenerated]
private readonly string k__BackingField;
public override string ReadOnly
{
[CompilerGenerated]
get
{
return k__BackingField; // the one in Test
}
}
}
除了可能添加警告之外,没有理智的方法可以解决这个问题.只是尝试在基础构造函数中添加一个`Console.WriteLine(ReadOnly);`,在分配给`ReadOnly`后你会看到一个不同的症状,它会调用我们知道的属性的后代getter.已分配,因此尝试打印"null".这里唯一合理的修复,如果有的话,编译后代类应该警告你实际上有两个不同的属性*实现*.
在任何人抱怨"谁在乎,你不应该这样做,因为它什么都不给你",然后考虑你可以在后代的重写属性中添加属性,这将与属性中的属性相结合.基类.所以*它*会给你一些东西,虽然它可能不是很多用途的功能,但它在序列化场景中可能很重要.
2> Matthew Wats..:
解释这个的最简单方法是考虑编译器生成什么代码来实现它.
基类等同于:
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;
}
这里要注意的重要一点是派生类有它的OWN BACKING FIELD ReadOnly
- 它不会重用基类中的那个.
意识到这一点后,显然为什么被覆盖的属性为null.
这是因为派生类有自己的支持字段ReadOnly
,并且其构造函数不初始化该支持字段.
顺便说一句,如果你正在使用Resharper
它,它实际上会警告你,你没有ReadOnly
在派生类中进行设置:
"Get-only auto-property 'ReadOnly' is never assigned."