2010-06-26 3 views
0

TL; DR:Переопределить метод возвратного типа в производном классе без дженериков

Есть ли какой-нибудь способ, чтобы добавить абстрактный метод в качестве базового класса, который позволяет производным классам переопределить метод возвращаемого типа, без использование дженериков и без использования ключевого слова new?


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

Одна из задач, с которой я начал работать (и добилась хороших успехов), разрабатывает шаблон, который генерирует DTO для каждого объекта. Вдоль этих строк одна цель заключается в предоставлении моим объектам метода ToDTO(). В интересах универсального программирования я решил определить этот метод в общем базовом классе, и именно здесь начинается моя проблема.


Имейте в виду, что цель определения метода ToDTO() в базовом классе, потому что я ищу, чтобы создать общий репозиторий (с методом Fetch(), к примеру), что я хотел бы иметь работайте с CommonEntityBase, в отличие от конкретного объекта.


LLBLGen определяет его CommonEntityBase класс следующим образом:

public abstract partial class CommonEntityBase : EntityBase2 { 
    // LLBLGen-generated code 
} 

Мой первоначальный план был добавить свой метод к другому частичному классу так:

public abstract partial class CommonEntityBase { 
    public abstract CommonDTOBase ToDto(); 
} 

Я думал, что наследуется классы могли бы определять тип возвращаемого значения в своих методах как тип, полученный из возвращаемого типа базового класса, например:

public partial class PersonEntity : CommonEntityBase { 
    public override PersonDTO ToDto(){ return new PersonDTO(); } 
} 

но I was wrong.


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

public abstract partial class CommonEntityBase<T> : CommonEntityBase 
     where T : CommonDTOBase { 
    public abstract T ToDto(); 
} 

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

отдельные лица LLBLGen имеют такое определение:

public partial class PersonEntity : CommonEntityBase { 
    // LLBLGen-generated code 
} 

И в этом заключается моя проблема.Для того, чтобы мой метод к работе, я бы создать свой собственный частичный класс с этим определением:

public partial class PersonEntity : CommonEntityBase<PersonDTO> { 
    public override PersonDTO ToDto(){ return new PersonDTO(); } 
} 

Конечно, это не представляется возможным, потому что, as I now know,

Все из части [частичного класса], которые определяют базовый класс , должны согласовать, но части, которые опускают базовый класс, все еще наследуют базовый тип.


Третья вещь, которую я собирался попытаться просто перекрывая определение функции базового класса с new ключевого слова:

public abstract partial class CommonEntityBase { 
    public virtual CommonDTOBase ToDto(){ return null; } 
} 

public partial class PersonEntity : CommonEntityBase { 
    public new PersonDTO ToDto(){ return new PersonDTO(); } 
} 

Однако это поражение цели моего подхода полностью, как я хочу иметь доступ к PersonEntityToDTO() метод, когда он отличен как CommonEntityBase. При таком подходе, делая:

CommonEntityBase e = new PersonEntity(); 
var dto = e.ToDto(); 

приведет к dto будучи нуль, что я не хочу.


Я пришел через variouslinks обсуждали мой первый подход, и почему он не будет работать, и, как правило, указывая на мой общий подход в качестве решения в общем смысле. Однако в моей ситуации дженерики не работают.


Все это, чтобы узнать, возможно ли то, что я пытаюсь выполнить.

Есть ли способ добавить абстрактный метод к базовому классу, который позволяет производным классам переопределять возвращаемый тип метода без использования дженериков и без использования ключевого слова new?

Возможно, я подхожу к этому с неправильного угла, и есть какая-то другая техника, которая могла бы решить мои проблемы?


EDIT

Вот вариант использования для того, что я хотел бы достичь с субъектами, принимая Porges's подход:

public class BaseRepository<D,E> where D : CommonDTOBase where E : CommonEntityBase,new 
    public D Get(Guid id){ 
     var entity = new E(); 
     entity.SetId(id); 

     // LLBLGen adapter method; populates the entity with results from the database 
     FetchEntity(entity); 

     // Fails, as entity.ToDto() returns CommonDTOBase, not derived type D 
     return entity.ToDto(); 
    } 
} 
+0

У вас, похоже, есть некоторые концептуальные проблемы с «частичными» классами. Магии нет; Я думаю, что лучший способ подумать о них состоит в том, что компилятор просто накапливает весь код, определенный для частичного класса, в одно определение класса перед компиляцией. – porges

+0

Google «ковариантные типы возврата». –

ответ

0

Я не знаю LLBLGen , но я считаю, что вы могли бы решить свою проблему таким образом, введя интерфейс для хранения параметра типа:

public interface DTOProvider<T> where T : CommonDTOBase { 
    public T ToDTO(); 
} 

А потом для классов сущностей, сделайте следующее:

public partial class PersonEntity : CommonEntityBase, DTOProvider<PersonDTO> { 
    public PersonDTO ToDto() { return new PersonDTO(); } 
} 

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

public void DoSomethingWithDTO<T>(CommonBaseEntity entity) 
     where T : CommonDTOBase { 
    T dto = ((DTOProvider<T>) entity).ToDTO(); 
    ... 
} 

Конечно, вы можете позвонить ToDTO непосредственно без броска, когда у вас есть ссылка на один из сущностей полученных типов :

public void DoSomethingWithPersonDTO(PersonEntity entity) 
{ 
    PersonDTO dto = entity.ToDTO(); 
    ... 
} 

Если вы используете .NET Framework 4, вы можете использовать общую дисперсию, чтобы сделать интерфейс DTOProvider проще использовать из кода, который просто заботится о работе с CommonDTOBase объявляя типа коварианты DTO:

public interface DTOProvider<out T> where T : CommonDTOBase { 
    public T ToDTO(); 
} 

(Обратите внимание, что «вне».) Тогда ваш метод DoSomethingWithDTO не нужен параметр типа:

public void DoSomethingWithDTO(CommonBaseEntity entity) { 
    CommonDTOBase dto = ((DTOProvider<CommonDTOBase>) entity).ToDTO(); 
    ... 
} 

Заманчиво попытаться объявить : CommonBaseEntity, DTOProvider<T> о частичном классе CommonBaseEntity. К сожалению, это не сработает, потому что, когда частичные определения объединены, параметр типа переносится, и ваш тип CommonBaseEntity оказывается типичным типом, который, по-видимому, является тем, что в первую очередь привело вас к привязке.

+0

С помощью этого подхода, где 'CommonBaseEntity' получает свое определение для' ToDto() '? Если он наследует использование ': DTOProvider ', то мы сталкиваемся с моей первоначальной проблемой неспособности изменить типы возвращаемых данных, и если он наследует использование ': DTOProvider ', тогда мы должны включить дженерики в 'CommonBaseEntity'. – cmptrgeekken

4

Вместо:

public abstract partial class CommonEntityBase { 
    public abstract CommonDTOBase ToDto(); 
} 

public partial class PersonEntity : CommonEntityBase { 
    public override PersonDTO ToDto(){ return new PersonDTO(); } 
} 

Почему вы не просто возвращать DTO как это:

public abstract partial class CommonEntityBase { 
    public abstract CommonDTOBase ToDto(); 
} 

public partial class PersonEntity : CommonEntityBase { 
    // changed PersonDTO to CommonDTOBase 
    public override CommonDTOBase ToDto(){ return new PersonDTO(); } 
} 

Я думаю, что это более идиоматических для OO кода. Есть ли причина, по которой вам нужно знать точный тип DTO?

Смежные вопросы