В C# дженерики, интерфейсы и наследование

у меня два интерфейса:

public interface IAmA
{
}

public interface IAmB<T> where T : IAmA
{
}

и два класса, реализующие эти интерфейсы следующим образом:

public class ClassA : IAmA
{
}

public class ClassB : IAmB<ClassA>
{
}

при попытке использовать эти классы, как показано ниже:

public class Foo
{
    public void Bar()
    {
        var list = new List<IAmB<IAmA>>();
        list.Add(new ClassB());
    }
}

Я получаю эту ошибку компилятора:

cannot convert from 'ClassB' to 'IAmB<IAmA>'

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

public class ClassB : IAmB<IAmA>
{
}

но я должен быть параметр типа IAmB<> на ClassB реализация IAmA.

5 ответов


быстрый ответ, что вы can сделайте то, что вы просите, объявив параметр типа IAmB<T> как ковариантные, только если тип используется в качестве возвращаемого типа:

public interface IAmB<out T> where T : IAmA
{
    T SomeMethod(string someparam);
}

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

вы не сможете использовать T в качестве параметра. Следующее не будет компилироваться:

public interface IAmB<out T> where T : IAmA
{
    void SomeMethod(T someparam);
}

из документации

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

это не особенность компилятора. Предполагая, что вы можете объявить параметр ковариантного метода, ваш список будет содержать некоторые объекты, которые не мог ручка


быстрый ответ: сделайте общий тип ковариантным (см. в MSDN) в интерфейсе

public interface IAmB<out T> where T : IAmA
{
}

это решит проблему компилятора.

но это не ответ why спросил Панагиотис Канавос !


трюк делает ограничение типа T on IAmB<T> ковариантные С out ключевые слова:

public interface IAmB<out T> where T : IAmA
{
}

это позволяет использовать более конкретный тип, чем первоначально указанный, в этом случае позволяет назначить IAmB<ClassA> переменной типа IAmB<IAmA>.

для получения дополнительной информации см. раздел документация.


Я просто говорю, почему эта ошибка сообщается.

если IAmB метод

public interface IAmB<T> where T : IAmA
{
    void foo(T p);
}

public class ClassB : IAmB<ClassA>
{
    void foo(ClassA p)
    {
        p.someIntField++;
    }
}

и у нас есть еще один класс

public class ClassC : IAmB<ClassA2>
{
    void foo(ClassA2 p)
    {
        p.someOtherIntField++;
    }
}

и мы предполагаем,List<IAmB<IAmA>>.Add(T p) реализовать такой

IAmA mParam = xxxx;
void Add(IAmB<IAmA>> p){
    p.foo(mParam);
}

думаю все компилируется нормально. вы передаете экземпляр ClassB в List.Add, становится

void Add(IAmB<IAmA>> p){
    //p is ClassB now
    p.foo(mParam);//COMPILER CAN NOT MAKE SURE mParam fit ClassB.foo
}

его можно решить с помощью Контравариации и ковариации.

public interface IAmA
{
}

**public interface IAmB<out T> where T : IAmA
{
}**


public class ClassA : IAmA
{
}

public class ClassB : IAmB<ClassA>
{
}


public class Foo
{
    public void Bar()
    {
        var list = new List<IAmB<IAmA>>();
        **list.Add(new ClassB());**
    }
}

теперь вы не получите ошибку компилятора. Компилятор счастлив.