2015-07-24 2 views
6

Я ищу способ наследования дженериков с унаследованными параметрами - или, если это невозможно, самый близкий способ получить ту же функциональность.Наследование генераторов с унаследованными параметризациями

Рассмотрим следующий пример:

Класс B наследует от класса А

Класс D наследует от класса C

Теперь у меня есть один класс:

abstract class A<T> where T : C 

с конструктором:

public A(T t) 

Теперь я хотел бы расширить класс А, как так:

class B<D> : A<C> 

Создание конструктора:

public B(D t) : base(t){ /*stuff here*/} 

Однако, это вызывает ошибку во время компиляции, так как D не C. Таким образом, мой два вопросы:

a) Есть ли чистый способ сделать это? В худшем случае я думаю, что я могу заменить D на C небольшими проблемами, но, возможно, есть причина, почему это не безопасная идея?

b) Должен ли я даже явно указывать свои дженерики в определении дочернего класса или есть ли более чистый способ сделать это с ограниченными типами?

+2

Не видите сценарий, который вы использовали бы, но «A где T1: T2, где T2: C' и' B : A 'должен делать трюк. – MarcinJuraszek

ответ

2

Добавить, где ограничение на класс B

class B<D> : A<C> where D : C 

Чтобы ответить вам вопросы:

а) с исправлением в этом ответе, ваш подход является довольно чистым до сих пор, если чуть-чуть более абстрактно (из-за наименований в основном).

b) Вы должны добавлять только D-тип типа D к вашему дочернему классу B, если сам дочерний класс либо добавляет значение в общий способ, относящийся к подклассам C, которые являются экземплярами D; или если дочерний класс сам является неполным, ожидается, что он будет расширен и потребует подклассы знаний C как D. В этом случае вы должны отметить его абстрактным.

UPDATE:

Я хотел бы добавить еще одну вещь об этом. В вышеприведенной сигнатуре параметр TA<T> будет C в тех элементах A, которые используют тип T. Это может быть проблемой, как показано в следующем примере:

public class C {} 

public class F : C {} 

public class E : C {} 

public class A<T> where T : C 
{ 
    protected T cSubclass; 
    public void SetCSubclass(T cSubclass) { this.cSubclass = cSubclass; } 
} 

public class B<D> : A<C> where D : C 
{ 
    public D GetCSubclass() 
    { 
     return this.cSubclass; 
    } 
} 

Код в этом примере не будет компилироваться.Вы получите следующее сообщение об ошибке компиляции:

error CS0266: Cannot implicitly convert type 'C' to 'D'. An explicit conversion exists (are you missing a cast?) 

Однако, устранена ошибка компиляции, если мы изменим класс B к следующему:

public class B<D> : A<D> where D : C 
{ 
    public D GetCSubclass() 
    { 
     return this.cSubclass; 
    } 
} 

Причина заключается в том, что D должен быть конкретный подкласс C , однако в прежней версии мы ограничили A только любой формой C, включающей сам себя и любой из его подклассов. Поэтому мы могли бы назвать new B<F>.SetCSubclass(new E());, который был бы другого типа, чем ожидал бы возврат GetCSubclass. В последней версии мы указали D как аргумент типа для использования в форсировании A B<F>.SetCSubclass, чтобы принимать только экземпляры F.

Это обеспечивает дополнительную степень безопасности типа, которую может ожидать разработчик, использующий этот тип шаблонов.

+0

@ user650261 Я хотел добавить еще одну вещь об этом. Вы уверены, что вам не нужен «класс B : A где D: C {}". Разница с этой альтернативной сигнатурой заключается в том, что в любом месте A возвращает или принимает T в подклассах B, T будет D. В сигнатуре, представленной в моем ответе, T будет C в тех членах A, которые могут быть не такими, какие вы в конечном счете хотите, поскольку, если T используется для параметра метода, вы можете получить подкласс C, который не является подклассом D. –

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