Почему переопределенное свойство get-only остается равным null при установке в конструкторе базового класса?
Я попробовал следующий пример:
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 остается нулевым. Но почему? Я действительно не понимаю, может ли кто-нибудь объяснить мне, почему это происходит? По крайней мере, я ожидал бы и ошибки, что свойство только для чтения не может быть установлено за пределами класса-владельца.
Ответы
Ответ 1
Компилятор рассматривает это, как показано ниже; в основном, код в конструкторе записывается в исходное поле поддержки в TestBase
. Кажется, что ваш сценарий не поддерживается, но... Я задаюсь вопросом, рассмотрела ли языковая команда этот случай.
BTW: если вы когда-нибудь захотите узнать, что делает компилятор с кодом: 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
}
}
}
Ответ 2
Самый простой способ объяснить это - рассмотреть, какой код генерирует компилятор для его реализации.
Базовый класс эквивалентен этому:
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."
Ответ 3
Поскольку базовый класс определяет поведение, которое должен придерживаться любой класс (см. Принцип подстановки Лискова), но не ограничивает (и не должен) ограничивать его поведение.
Базовый класс может определять только поведение, и он не может (и не должен) указывать, что поведение "не должно быть" (т.е. "Не должно быть доступно для записи на конкретных уровнях").
"Только для чтения" - это только то значение, которое вы ему назначаете. В С# нет каких-либо релевантных гарантий. У вас может быть свойство get-only, которое изменяется каждый раз, или свойство get/set, в котором установщик выдает InvalidOperationException ( "Этот экземпляр доступен только для чтения").