Как ограничить переопределенное свойство только для чтения - PullRequest
0 голосов
/ 10 мая 2011

У меня следующий код.

   class A
    {
        public virtual int BoardSize { get; set; }
    }

    class B : A
    {
        public override int BoardSize
        {
            get{return 100;}
        }
    }

    Class Client
    {
        B b = new B();
        b.BoardSize = 55;
    }

В базовом классе свойство доступно для чтения / записи. но в производном классе он доступен только для чтения и всегда должен возвращать 100. Хотя, когда я запускал код, я обнаружил, что он всегда возвращает 100, как и ожидалось.

Но я не хочу, чтобы клиент устанавливал BoardSize в классе B. Поэтому он не должен позволять клиенту писать b.BoardSize = 55 вообще.

Как это возможно?

Ответы [ 5 ]

5 голосов
/ 10 мая 2011

Краткий ответ: Ну ... вы не можете!
Длинный ответ:
Вы можете скрыть реализацию BoardSize от базового класса, используя new:

class B : A
{
    public new int BoardSize
    {
        get{return 100;}
    }
}

Это сделает следующий код недействительным:

B b = new B();
b.BoardSize = 55;

Но это только усугубит ситуацию!
Действителен следующий код:

A a = new B();
a.BoardSize = 55;
Console.WriteLine(a.BoardSize);
Console.WriteLine(((B)a).BoardSize);

и напечатано

55
100

Вывод:
Вы можете использовать new для достижения своей цели, но это создаст серьезные проблемы в вашем коде.
ЕслиВы не можете изменить A, и B необходимо получить из A, затем используйте этот код:

class B : A
{
    public override int BoardSize
    {
        get{return 100;}
        set{throw new NotSupportedException();}
    }
}

Это соответствует .NET Framework.Например, ReadOnlyCollection выдает это исключение при вызове Add (после приведения его к ICollection<T>).

1 голос
/ 10 мая 2011

У вас есть два варианта.

1) Бросить исключение в сеттер:

class A
{
    public virtual int BoardSize { get; set; }
}

class B : A
{
  public override int BoardSize
  {
    get { return 100; }
    set {
      throw new NotSupportedException("Cannot set BoardSize on class B.");
    }
  }
}

Если у вашего кода есть путь, по которому он может попытаться установить BoardSize для экземпляра B, вы должны быть готовы обработать это исключение.

2) Разрешить только производному классу устанавливать BoardSize:

class A
{
  public int BoardSize { get; protected set; }
}

class B : A
{
  public B()
  {
    BoardSize = 100;
  }
}

class C : A
{
  public void SetBoardSize(int boardSize)
  {
    BoardSize = boardSize;
  }
}
0 голосов
/ 27 июля 2011

Если производному классу может потребоваться изменить внешний вид элемента (например, изменить свойство с «чтение / запись» и «только для чтения») или иметь метод в производном типе, возвращающем более производноечем метод с аналогичным именем в базовом типе), то сам член должен быть не виртуальным, но его реализация должна просто обернуть вызов в защищенную виртуальную реализацию.Производные типы могут скрывать членов базового класса, которые вызывают защищенную виртуальную реализацию, но не должны помещать туда значимую логику.

Таким образом:

class A
{
    public int BoardSize { get {return GetBoardSize(); set {SetBoardSize(value); }
    protected abstract int GetBoardSize();
    protected abstract void SetBoardSize(int newSize);
    public abstract bool IsResizable { get; }
}

class B : A
{
    public new int BoardSize { get {return GetBoardSize(); }
    protected override int GetBoardSize() { return 100; }
    protected override void SetBoardSize(int newSize)
        { throw new NotSupportedException("Board is not resizable."); }
    public override bool IsResizable { get {return false;} }
}

Попытка установить BoardSize для переменнойтипа B потерпит неудачу во время компиляции на том основании, что свойство доступно только для чтения.Сохранение ссылки на B в переменной типа A и попытка установить BoardSize для этого скомпилируют, но вызовут исключение во время выполнения.Класс должен быть задокументирован, чтобы указать, что код, который хочет установить BoardSize, должен быть подготовлен к тому факту, что плата не может быть изменяемого размера, и что если код вызывающей стороны сможет сделать что-то разумное в этом случае, он должен проверитьIsResizable, прежде чем пытаться установить BoardSize.

0 голосов
/ 10 мая 2011

Проверьте, работает ли это:

class A
{
    public virtual int BoardSize { get; protected set; }
}
0 голосов
/ 10 мая 2011

То, что вы хотите - это новое свойство в B, которое скрывает одно в A:

class B : A
{
    public new int BoardSize
    {
        get{return 100};
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...