2008-09-23 5 views
5

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

Может кто-нибудь попытаться объяснить мне, почему следующая настройка не компилируется?

public class ClassA 
{ 
    ClassB b = new ClassB(); 

    public void MethodA<T>(IRepo<T> repo) where T : ITypeEntity 
    { 
     b.MethodB(repo); 
    } 
} 

public class ClassB 
{ 
    IRepo<ITypeEntity> repo; 

    public void MethodB(IRepo<ITypeEntity> repo) 
    { 
     this.repo = repo; 
    } 
} 

Я получаю следующее сообщение об ошибке:
не может конвертировать из IRepo < 'T> в IRepo <' ITypeEntity>

Methoda вызывается с IRepo < «DetailType> параметр объекта, где DetailType наследуется от ITypeEntity ,

Я продолжаю думать, что это должно компилироваться, поскольку я ограничиваю T в MethodA типом ITypeEntity.

Любые мысли или отзывы были бы чрезвычайно полезными.

Спасибо.

Редактировать: У Nick R есть большое предложение, но, к сожалению, в моем контексте у меня нет возможности сделать ClassA Generic. ClassB может быть.

ответ

3

Наследование не работает при использовании дженериков. Как отмечает Smashery, даже если TypeA наследует от TypeB, myType < TypeA> не наследует от myType < TypeB>.

Таким образом, вы не можете сделать вызов метода, определенного в качестве Methoda (MyType < TypeB> б) ожидающей MyType < TypeB> и дать ему MyType < TypeA> вместо этого. Соответствующие типы должны соответствовать точно. Таким образом, следующий не будет компилироваться:

myType<TypeA> a; // This should be a myType<TypeB>, even if it contains only TypeA's 

public void MethodB(myType<TypeB> b){ /* do stuff */ } 

public void Main() 
{ 
    MethodB(a); 
} 

Так что в вашем случае, вам нужно будет пройти в IRepo < ITypeEntity> к MethodB, даже если он содержит только DetailTypes. Вам нужно будет сделать некоторое преобразование между ними. Если вы использовали общий IList, вы можете сделать следующее:

public void MethodA<T>(IList<T> list) where T : ITypeEntity 
{ 
    IList<T> myIList = new List<T>(); 

    foreach(T item in list) 
    { 
    myIList.Add(item); 
    } 

    b.MethodB(myIList); 
} 

Надеюсь, это будет полезно.

2

Проблема заключается в сложной ситуации, чтобы окунуться. DetailType может наследоваться от ITypeEntity, но на самом деле это не ITypeEntity. Ваша реализация DetailType может ввести различные функциональные возможности, поэтому DetailType реализует ITypeEntity, но не соответствует ITypeEntity. Я надеюсь, что это имеет смысл ...

0

Если B является подклассом A, это не означает, что Class<B> является подклассом Class<A>. Итак, по этой же причине, если вы говорите: «T - это ITypeEntity», это не означает, что «IRepo<T> - это IRepo<ITypeEntity>». Возможно, вам придется написать свой собственный метод конвертации, если вы хотите, чтобы это работало.

0

T - переменная типа, которая будет связана с использованием частичного типа. Ограничение гарантирует, что этот тип будет представлять подмножество типов, реализующих ITypeEntity, за исключением других типов, реализующих интерфейс.

0

во время компиляции, даже если вы сдерживаете его, компилятор знает только, что T в MethodA является ссылочным типом. он не знает, к какому типу он привязан.

3

Ну, это компилируется нормально. Я в основном переделал классы для получения общих параметров. Это может быть хорошо в вашем контексте.

public interface IRepo<TRepo> 
{ 
} 

public interface ITypeEntity 
{ 
} 


public class ClassA<T> where T : ITypeEntity 
{ 
    ClassB<T> b = new ClassB<T>(); 
    public void MethodA(IRepo<T> repo) 
    { 
     b.MethodB(repo); 
    } 
} 
public class ClassB<T> where T : ITypeEntity 
{ 
    IRepo<T> repo; 
    public void MethodB(IRepo<T> repo) 
    { 
     this.repo = repo; 
    } 
} 
+0

Если вы generic'ing до первого класса 2-ой также должны быть generic'ed – 2008-09-23 22:53:33

+0

К сожалению у меня нет возможности сделать CLASSA универсальный класс, как это ASP .Net User Контроль, и я действительно не хочу пытаться выяснить эту проблему. Спасибо за вашу мысль. Это было вполне разумно на основе ограниченного контекста, который я дал. – 2008-09-23 23:04:36

0

Это излишнее использование дженерик, если T может только когда-либо будет экземпляр ITypeEntity вы не должны использовать дженерик.

Дженерики предназначены для тех случаев, когда у вас есть несколько типов, которые могут быть внутри чего-то.

1

I get the following error: cannot convert from IRepo<'T> to IRepo<'ITypeEntity>

Вы получаете эту ошибку компиляции, потому что IRepo<T> и IRepo<ITypeEntity> являются не то же самое. Последнее является специализацией первого.IRepo<T>является общим определением типа, где параметр типа Т представляет собой заполнитель, и IRepo<ITypeEntity> является constructured универсального типа определения универсального типа, где параметр Т типа от указан быть ITypeEntity.

I keep thinking that this should compile as I'm constraining T within MethodA to be of type ITypeEntity.

where ограничение здесь не поможет, потому что contrains только тип вы можете предоставить для T в колл-сайтов для MethodA.

Вот терминология из документации MSDN (см Generics in the .NET Framework), которые могут помочь:

  • Родовое определение типа является класса, структуры или интерфейса декларация, которая функционирует как шаблон, с заполнители для типов , которые могут содержать или использовать. Например, класс Dictionary<<K, V> может содержать два типа: ключи и значения. Потому что это всего лишь шаблон, вы не можете создать экземпляры класса, структуры или интерфейса, который является определением общего типа .

  • Параметры типового типа или параметры типа являются заполнителями в общим типом или методом. Тип генератора Dictionary<K, V> имеет два типа параметров, K и V, которые представляют типы его ключей и значения .

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

  • Аргументом общего типа является любой тип , который заменяет общий параметр типа .

  • Общий термин общий тип включает оба построенных типа и определения общего типа.

  • Ограничения, установленные на параметры типового типа. Например, для примера вы можете ограничить параметр типа типами, которые реализуют IComparer<T> общий интерфейс , чтобы можно было указать экземпляры этого типа. Вы можете также ограничить параметры типа типами, которые имеют конкретный базовый класс , которые имеют конструктор по умолчанию или являются ссылочными типами или типами значений. Пользователи типа не могут заменить аргументы типа , которые не удовлетворяют ограничениям .

1

@monoxide Пожалуйста, смотрите «s вопрос

И as I said there, проверяя ряд Эрика Липперта постов на контрвариации и ковариации для дженериков будет сделать много это понятнее.

0

В контексте обертывания головы вокруг общих методов позвольте мне дать вам простую общую функцию. Это общий эквивалент VB IIf() (Immediate if), который сам по себе является плохой имитацией тройного оператора C-стиля (?). Это не полезно ни для чего, так как реальный тернарный оператор лучше, но, возможно, он поможет вам понять, как генерируются общие функции и в каких контекстах они должны применяться.

T IIF<T>(bool Expression, T TruePart, T FalsePart) 
{ 
    return Expression ? TruePart : FalsePart; 
} 
Смежные вопросы