Использование скрытия членов (`new`) для получения более конкретного типа возвращаемого значения - PullRequest
3 голосов
/ 08 июля 2010

Я собираюсь использовать 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 ]

2 голосов
/ 08 июля 2010

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

Это просто сохранить печать в любом случае, и это снизит вашу производительность, поскольку 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();
2 голосов
/ 08 июля 2010

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

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

...