Использование скрытия членов (`new`), чтобы получить более конкретный тип возврата

Я собираюсь использовать new переопределить члены в подклассах, чтобы сделать доступным более конкретный тип возвращаемого значения. Вопрос в том, хорошая ли это идея или она требует неприятностей?

Проблема, которую нужно решить, - это когда существует несколько "зеркальных" иерархий классов, где типы из каждого иерархического слоя ссылаются на один и тот же уровень другой иерархии. Это сложно объяснить, поэтому вот пример:

class A1 {}
class A2 : A1 {}
class A3 : A2 {}

abstract class B1
{
  protected A1 myA;

  A1 MyA { get { return myA; } }
}

abstract class B2 : B1
{
  new A2 MyA { get { return (A2)myA; } }
}

class B3 : B2
{
  new A3 MyA 
  { 
    get { return (A3)myA; } 
    set { myA = value; }
  }
}

Есть код, который обрабатывает только абстрактный "уровень" 1 (A1 / B1 и т.д.), также как и для любого другого уровня. Было бы хорошо, если бы каждый "уровень" видел типы как можно более специфично. В настоящее время я определил типы на самом верхнем уровне и все время понижаю его в вызывающем коде. Иногда я добавляю другого члена для некоторого "уровня", используя другое имя, это приводит к множеству избыточных членов. Я хочу сделать это чище.

(Дженерики здесь не вариант, потому что B2 не будет полезным без знания B3 (для этого потребуются дополнительные интерфейсы). И если есть много типов (например, также C1/2/3, D1/2/3 ...), будет слишком много общих аргументов. Технически это будет работать, но это будет очень сложно.)

Дополнительно я буду сериализовать некоторые из этих классов (DataContractSerializer), а некоторые будут сопоставлены с базой данных с помощью NHibernate.

Кто-нибудь уже делал что-то подобное? Это рекомендуется? Существуют ли другие подходы для решения такой же проблемы?

Изменить: Я немного изменил код из-за обсуждения о приведении и реализации свойств. Это на самом деле не вопрос здесь. Я просто попытался написать как можно меньше кода, чтобы сделать короткий пример.

2 ответа

Я бы не стал это делать.

В любом случае, это просто сохранит набор текста и снизит вашу производительность B3.MyA по существу сделаю return (A3)(A2)this.MyA;, Слишком много ненужных приведений, пока вы можете сделать b3.MyA as A3 напрямую, только если это необходимо.

Также, если вы забыли инициализировать MyA в B3 к объекту типа A3, ваше приведение не удастся, так как это будет объект типа A1 или же A2 или что угодно. Вы можете легко выстрелить себе в ногу.

Редактировать: смысл того, что я написал: это НЕ типичный способ сделать это, и никогда не будет. Вы можете минимизировать риск этого (например, как показывает ваш отредактированный код), но возможность все еще существует. ИМХО преимущества такой конструкции не оправдывают опасности.

Edit2: примечание к вашему Technically it would work, but it just gets very complicated. в общем подходе.

Не так сложно ИМХО. Для сравнения:

public abstract class B1<TA, TC> where TA: A1 where TC: A1
{
    public TA MyA { get; protected set; }
    public TC MyC { get; protected set; }
}

public abstract class B2<TA, TC> : B1<TA, TC> where TA : A2 where TC : A2
{
}

public class B3 : B2<A3, A3>
{
}

Меньше кода, больше безопасности и помните, что вы всегда можете использовать using Скажите, если вам не нравится слишком много печатать:

using ClassicB2 = MemberHiding.B2<MemberHiding.A3, MemberHiding.A3>;
ClassicB2 b3 = new B3();

Я думаю, что вы делаете это задом наперед... Даункауты потерпят неудачу, вы должны создать это с помощью аутсайтов Реализация IEnumerable является наиболее распространенным примером этого шаблона.

Самый производный тип должен отвечать за создание самого производного результата, тогда виртуальные методы в базовом типе могут быть переопределены, чтобы возвращать самый производный результат через восходящий поток (который может быть неявным).

Другие вопросы по тегам