Сделать свойство доступным только для чтения в производном классе - PullRequest
6 голосов
/ 10 декабря 2010

Я переопределяю свойство в своем производном классе, которое я хотел бы сделать его доступным только для чтения.Компилятор C # не позволит мне изменить модификаторы доступа, поэтому он должен оставаться открытым.

Какой лучший способ сделать это?Должен ли я просто бросить InvalidOperationException в set { }?

Ответы [ 6 ]

8 голосов
/ 10 декабря 2010

Если метод установки бросает InvalidOperationException в производном классе, это нарушает Принцип подстановки Лискова .По существу делает использование сеттера контекстным по отношению к типу базового класса, что существенно исключает значение полиморфизма.

Ваш производный класс должен уважать контракт своего базового класса.Если установщик не подходит для всех обстоятельств, он не относится к базовому классу.

Один из способов обойти это - немного разбить иерархию.

class C1 { 
  public virtual int ReadOnlyProperty { get; } 
}
class C2 { 
  public sealed override int ReadOnlyProperty { 
    get { return Property; }
  }
  public int Property {
    get { ... }
    set { ... }
  }
}

Ваш тип, с которым у вас проблемы, может унаследовать C1 в этом сценарии, а остальные могут переключиться на C2

3 голосов
/ 10 декабря 2010

Вы можете скрыть исходную реализацию и вернуть базовую реализацию:

class Foo
{
    private string _someString;
    public virtual string SomeString 
    {
        get { return _someString; }
        set { _someString = value; }
    }
}

class Bar : Foo
{
    public new string SomeString 
    { 
        get { return base.SomeString; }
        private set { base.SomeString = value; }
    }
}

namespace ConsoleApplication3
{
    class Program
    {
        static void Main( string[] args )
        {
            Foo f = new Foo();
            f.SomeString = "whatever";  // works
            Bar b = new Bar();
            b.SomeString = "Whatever";  // error
        }
    }
}

Тем не менее ,. как намекал Джаред, это довольно странная ситуация. Почему бы вам не сделать свой установщик частным или защищенным в базовом классе для начала?

1 голос
/ 03 ноября 2015

Я думаю, что в этой ситуации лучшее решение - новое ключевое слово

public class Aclass {        
    public int ReadOnly { get; set; }
}

public class Bclass : Aclass {        
    public new int ReadOnly { get; private set; }
}
0 голосов
/ 17 декабря 2012

В некоторых случаях описанный вами шаблон оправдан. Например, может быть полезно иметь абстрактный класс MaybeMutableFoo, из которого производные подтипы MutableFoo и ImmutableFoo. Все три класса включают свойство IsMutable и методы AsMutable(), AsNewMutable() и AsImmutable().

В этой ситуации было бы вполне уместно, чтобы MaybeMutableFoo предоставил свойство чтения-записи, если его контракт явно указывает, что установщик может не работать, если IsMutable не вернет true. Объект с полем типа MaybeMutableFoo, который содержит экземпляр ImmutableFoo, может быть полностью доволен этим экземпляром до тех пор, пока ему не понадобится запись в него, после чего он заменит поле на объект, возвращенный через AsMutable(), а затем использовать его в качестве изменяемого foo (он знал бы, что он изменчив, поскольку он только что заменил его). Включение в MaybeMutableFoo установщика позволит избежать необходимости выполнять любые будущие типовые трансляции на поле после того, как оно было сделано для ссылки на изменяемый экземпляр.

Лучший способ разрешить такой шаблон - избегать реализации виртуальных или абстрактных свойств, а вместо этого реализовывать не виртуальные свойства, чьи методы получения и установки устанавливаются в цепочку с виртуальными или абстрактными методами. Если один имеет базовый класс

public class MaybeMutableFoo
{
    public string Foo {get {return Foo_get();} set {Foo_set(value);}
    protected abstract string Foo_get();
    protected abstract void Foo_set(string value};
}

тогда производный класс ImmutableFoo может объявить:

    new public string Foo {get {return Foo_get();}

, чтобы сделать его свойство Foo доступным только для чтения, не мешая возможности переопределения кода для абстрактных методов Foo_get() и Foo_set(). Обратите внимание, что замена только для чтения Foo не меняет поведение свойства get; версия только для чтения объединяет тот же метод базового класса, что и свойство базового класса. Поступая таким образом, вы гарантируете наличие одной точки исправления для изменения метода получения свойства и одной точки для изменения метода установки, и эти точки исправления не изменятся, даже если само свойство будет переопределено.

0 голосов
/ 10 декабря 2010

Просто определите свойство с новым ключевым словом и не предоставляйте метод Setter в производном классе.Это также скрывает свойство базового класса, однако, как уже упоминалось в mjfgates, можно получить доступ к свойству базового класса, назначив его экземпляру базового класса.Это полиморфизм.

0 голосов
/ 10 декабря 2010

Вы не можете скрывать публичные члены вашего базового класса в C #, потому что кто-то может взять экземпляр вашего производного класса, вставить его в ссылку на базовый класс и получить доступ к свойству таким образом.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...