Использование скрытия членов (`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
является наиболее распространенным примером этого шаблона.
Самый производный тип должен отвечать за создание самого производного результата, тогда виртуальные методы в базовом типе могут быть переопределены, чтобы возвращать самый производный результат через восходящий поток (который может быть неявным).