#c# #c#-9.0
Вопрос:
У меня возникла проблема с автоматической инициализацией свойств с ковариантным типом возврата. В конструкторе базового класса нет исключения, но свойство auto остается пустым.
Я нашел обходной путь, переопределив свойство и выполнив приведение в теле get (см. Комментарий ниже).
У вас есть объяснение этому поведению ? И есть ли лучший обходной путь ?
С уважением,
PS : Я совершил ошибку. Конечно, переопределенное свойство должно быть окончательной собственностью, а не базовой собственностью.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.WriteLine(new FinalClass(new FinalProperty()).MyProperty == null); // true
}
}
public class BaseClass
{
public BaseClass(BaseProperty prop)
{
MyProperty = prop;
Console.WriteLine($"ctor A : {MyProperty == null}"); // true
}
public virtual BaseProperty MyProperty { get; }
}
public class FinalClass : BaseClass
{
public FinalClass(BaseProperty prop)
: base(prop)
{ }
public override FinalProperty MyProperty { get; }
//public override FinalProperty MyProperty
//{
// get { return (FinalProperty)base.MyProperty; }
//}
}
public class BaseProperty
{ }
public sealed class FinalProperty : BaseProperty
{ }
Комментарии:
1. Это потому, что вы переопределяете
MyProperty
входFinalClass
и устанавливаете тот, который вBaseClass
. Если вы собираетесь переопределить его, то вам следует установить его вFinalClass
конструкторе. Также здесь нет ковариации, потому что вы не используете дженерики.2. Как это связано с ковариантными типами возвращаемых данных?
MyProperty
у inFinalClass
тот же тип,MyProperty
что и у inBaseClass
.3. В частности, проблема заключается в том, что вы переопределяете
get
so только тогда, когда устанавливаете его в полеBaseClass
оно установлено в скрытое поле в этом классе, но переопределенное попадание в полеFinalClasss
будет извлекаться из нового скрытого поля, определенного вFinalClass
Ответ №1:
Почему это происходит — для каждого MyProperty
переопределенного в дочерних классах с помощью компилятора автоматического свойства будет создано новое резервное поле для этого класса, т. е. следующее:
public class FinalClass : BaseClass
{
public FinalClass(FinalProperty prop)
: base(prop)
{ }
public override FinalProperty MyProperty { get; }
}
Будет преобразован компилятором во что-то вроде:
public class FinalClass : BaseClass
{
[CompilerGenerated]
private readonly FinalProperty <MyProperty>k__BackingField;
public virtual FinalProperty MyProperty
{
[PreserveBaseOverrides]
[CompilerGenerated]
get
{
return <MyProperty>k__BackingField;
}
}
public FinalClass(FinalProperty prop)
: base(prop)
{
}
}
Таким BaseClass(BaseProperty prop)
образом, вызываемое base(prop)
будет устанавливать резервное поле, сгенерированное для MyProperty
in BaseClass
, в то время FinalClass.MyProperty
как будет использоваться сгенерированное для него.
Другим возможным обходным путем является использование универсальных методов вместо ковариантных возвратов:
public class BaseClass<T> where T: BaseProperty
{
public BaseClass(T prop)
{
MyProperty = prop;
Console.WriteLine($"ctor A : {MyProperty == null}"); // true
}
public T MyProperty { get; }
}
public class FinalClass : BaseClass<FinalProperty>
{
public FinalClass(FinalProperty prop)
: base(prop)
{ }
}
Ответ №2:
Хотя вы переопределили MyProperty
FinalClass
, сделав это:
public override FinalProperty MyProperty { get; }
Тот факт, что это автоматическое свойство, все равно создает дополнительное поле для резервного FinalClass
копирования . Давайте уберем синтаксический сахар из автоматических свойств, чтобы увидеть, что происходит:
public class BaseClass
{
public BaseClass(BaseProperty prop)
{
// notice that when setting a get-only field in the constructor,
// you are actually setting its backing field
backingFieldMyPropertyInBaseClass = prop;
Console.WriteLine($"ctor A : {MyProperty == null}"); // true
}
private BaseProperty backingFieldMyPropertyInBaseClass;
public virtual BaseProperty MyProperty {
get { return backingFieldMyPropertyInBaseClass; }
}
}
public class FinalClass : BaseClass
{
public FinalClass(BaseProperty prop)
: base(prop)
{ }
private FinalProperty backingFieldMyPropertyInFinalClass;
public override FinalProperty MyProperty {
get { return backingFieldMyPropertyInFinalClass; }
}
}
Обратите внимание, как backingFieldMyPropertyInFinalClass
никогда не устанавливается.
Создание MyProperty
в FinalClass
ответ base.MyProperty
исправляет это, потому base.MyProperty
что на самом деле возвращает поле, которое вы хотите — backingFieldMyPropertyInBaseClass
. Вы также можете заставить его печатать false Main
, установив MyProperty
в конструкторе значение FinalClass
, чтобы оно backingFieldMyPropertyInFinalClass
было установлено.
Комментарии:
1. Я не могу добавить защищенный задатчик в MyPpoperty , потому что ковариантный тип возврата запрещает это. Я установил MyProperty в конструкторе FinalClass , но я хотел бы иметь возможность использовать MyProperty и в конструкторе базового класса, потому что он используется другими свойствами, инициализированными в конструкторе базового класса.
2.@R1Tech Ну, в таком случае ваш дизайн проблематичен.
BaseClass
невозможно безопасно установить переопределенную версиюMyProperty
, потому что вы переопределили ее более специализированным типом иBaseClass
не знаете, что это за тип. Смотрите конец ответа Гуру Строна, чтобыBaseClass
узнать, каков тип переопределенного свойства.3. Но я хотел бы иметь возможность инициализировать базовое свойство в базовом конструкторе и использовать его в этом конструкторе, затем инициализировать конечное свойство в конечном конструкторе и использовать его. И
BaseClass
он не знает, что это за тип и что это за типMyProperty
.