2017-02-06 11 views
1

У меня есть ситуация, когда я хочу, чтобы нормальное свойство отображалось только в производном классе со значением по умолчанию. Я использую ключевое слово нового для этой цели следующим образом:Изменить свойство BaseClass на readonly в DerivedClass с новым и переопределить

public abstract class BaseClass 
{ 
    public virtual string SomeInfo { get; set; } 
} 
public class DerivedClass1 : BaseClass 
{ 
    public new string SomeInfo => "ChildInfo1"; // C# 6.0 equivalent of { get { return "ChildInfo1"; } } 
} 

Он отлично работает, и new DerivedClass1().SomeInfo не может быть назначен - это неизменяемый. Я понимаю, что можно было бы получить доступ к нему через базовый класс:

BaseClass b1 = new DerivedClass1(); 
b1.SomeInfo = "ChildInfo1 changed"; 

Я просто хочу неспособный пользователю изменить его случайно, и через базовый класс было бы нарочно в таком случае это acceptible.

Однако если производный класс будет так:

public class DerivedClass2 : BaseClass 
{ 
    public override string SomeInfo => "ChildInfo2"; 
} 

Тогда это свойство будет доступно, и вы могли бы, казалось бы изменить его, но он не будет изменен, и я хотел бы понять, почему?

var d2 = new DerivedClass2(); 
d2.SomeInfo = "ChildInfo2 changed"; 
Console.WriteLine(d2.SomeInfo); // output: ChildInfo2 

UPDATE:
Я добавил новый ответ, как третий вариант, пожалуй, лучше всего.

+1

* «Я просто хочу, чтобы неспособный пользователю изменить его случайно, и через базовый класс было бы нарочно в таком случае это acceptible» * Это «специально», если программист вызывает метод, принимающий параметр «BaseClass», где - неизвестный программисту - этот метод меняет «SomeInfo»? В любом случае то, что вы предлагаете, нарушает [принцип Лисковского подмены] (https://en.wikipedia.org/wiki/Liskov_substitution_principle) и должно рассматриваться с подозрением. –

+0

Я думал, что это может нарушить LSP. Есть ли другой (лучший) способ достичь этого, не нарушая его? – borisdj

+0

Ну, вы могли бы создать интерфейс, который был доступен только для чтения, и передать его, но, конечно, это потребует модификации существующих методов, поэтому это может быть не вариант. –

ответ

0

Вам следует избегать использования «новой» в этой ситуации. Определение в производном классе, вместо этого, только для чтения свойство таким образом:

public override string SomeInfo { get { return "ChildInfo"; } } 

Вы все еще можете установить свойство, но он всегда будет возвращать значение по умолчанию «ChildInfo».

+0

Разве это не то же самое? Это дает предупреждение о том, что он скрывает унаследованный элемент и предлагает добавить новое или переопределить, если это было предназначено. Добавление нового явно явно скрывает, но поведение такое же? Итак, LSP по-прежнему нарушается. – borisdj

+0

@ Борис, ты прав. Я забыл написать «переопределить». Теперь исправлено. Да, в этом случае использование «нового» или «переопределения» получает тот же результат. Но, как вы знаете, использование «нового» нарушает LSP. – jacktric

+0

"new" и "override" не совпадают. У вашего первоначального сообщения не было ни одного ключевого слова этих двух, и это неявно означает скрыть, поэтому «новый» - это то же самое, что и никто. «переопределить», поскольку вы обновили сейчас, как и мой второй вариант, и в этом случае у вас есть доступ к установщику, но getter игнорирует его, поэтому он несовместим. Вы думаете, что вы изменили Свойство, но вы этого не сделали. Вот почему третий вариант, который я добавил как еще один ответ, лучше всего. – borisdj

1

В базовом классе у вас есть

public virtual string SomeInfo { get; set; } 

Это просто хорошее определение:

private string _someInfo; 
public string SomeInfo 
{ 
    get {return _someInfo;} 
    set {_someInfo = value;} 
} 

, если заменить его свойство Expression работоспособного переопределить свойство получить с

public string SomeInfo 
{ 
    get {return "ChildInfo2";} 
} 

Но вы не переопределяете свойство set, поэтому вы все равно можете установить личную переменную , но он ничего не меняет.

, если вы посмотрите на первый пример:

BaseClass b1 = new DerivedClass1(); 
b1.SomeInfo = "ChildInfo1 changed"; 

это бывает точным, то же самое, вы можете установить свойство, так как базовый класс имеет сеттер и устанавливает приватную переменную, но если вы пытаетесь выходное значение из SomeProperty, вы видите, что он не изменен и по-прежнему «ChildInfo1»

+0

Так сеттер устанавливает частное поле за ним, но getter возвращает то, что входит в состав метода Property, который фактически игнорирует это поле. Я понимаю сейчас. – borisdj

+0

@Boris да, это свойство с выражением тела означает: «всегда возвращать это постоянное значение» –

0

Теперь я сделал третьи опции, которые могут быть лучшими, задав значение по умолчанию в Constructor. Благодаря @MatthewWatson для привлечения внимания к LSP и @MaksimSimkin для объяснения.

public abstract class BaseClass 
{ 
    public string SomeInfo { get; set; } 
} 

public class DerivedClass1 : BaseClass 
{ 
    public DerivedClass1() 
    { 
     base.SomeInfo = "ChildInfo1"; 
    } 
    public new string SomeInfo => base.SomeInfo; 
} 

Это делает SomeInfo ReadOnly при доступе из DerivedClass, и если доступ через BaseClass это редактируемые и изменение регулярно применяется, и виден из обоих классов. Таким образом, нет «неизвестного» поведения.

PS Для более старых версий C# это эквивалентно:

public abstract class BaseClass 
{ 
    protected string _someInfo; 
    public string SomeInfo 
    { 
     get { return _someInfo; } 
     set { _someInfo = value; } 
    } 
} 

public class DerivedClass1 : BaseClass 
{ 
    public DerivedClass1() 
    { 
     _someInfo = "ChildInfo1"; 
    } 
    public new string SomeInfo 
    { 
     get { return _someInfo; } 
    } 
}