Реализация вложенных универсальных интерфейсов

У меня есть следующие классы/интерфейсы:

// Model
public class A : IA { }
// ModelLogic
public class B : IB<A> { }

// Model Interface
public interface IA { }
// ModelLogic Interface
public interface IB<T> where T : IA { }

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

IB<IA> foo = new B();

Я получаю следующую ошибку:

Cannot implicitly convert type 'B' to 'IB<IA>'. An explicit conversion exists (are you missing a cast?)

Может кто-нибудь объяснить, почему это невозможно?


person Bryan Sumter    schedule 27.04.2012    source источник
comment
Какую версию С# вы используете?   -  person Oded    schedule 27.04.2012
comment
B — это IB‹A›, а не IB‹IA›.   -  person Servy    schedule 27.04.2012


Ответы (3)


Хорошо, давайте заменим A на Fish, IA на IAnimal, B на Aquarium и IB<T> на IContainer<T>. И мы добавим элемент в IContainer<T> и вторую реализацию IAnimal:

// Model
public class Fish : IAnimal { }
public class Tiger : IAnimal { }
// ModelLogic
public class Aquarium : IContainer<Fish> 
{ 
    public Fish Contents { get; set; }
}

// Model Interface
public interface IAnimal { }
// ModelLogic Interface
public interface IContainer<T> where T : IAnimal 
{ 
    T Contents { get; set; }
}

IContainer<IAnimal> foo = new Aquarium(); // Why is this illegal?
foo.Contents = new Tiger(); // Because this is legal!

Вы можете поместить тигра в foo -- foo вводится как контейнер, который может содержать любое животное. Но вы можете поместить Рыбу только в Аквариум. Поскольку операции, которые вы можете законно выполнять с Aquarium, отличаются от операций, которые вы можете выполнять с IContainer<IAnimal>, типы несовместимы.

Нужная вам функция называется общая ковариантность интерфейса и поддерживается в C# 4, но вам нужно доказать компилятору, что вы никогда не посадите тигра в свой аквариум. . Что вы хотите сделать, это:

// Model
public class A : IA { }
// ModelLogic
public class B : IB<A> { }

// Model Interface
public interface IA { }
// ModelLogic Interface
public interface IB<out T> where T : IA { }

Обратите внимание на аннотацию ковариации на IB. Этот out означает, что T можно использовать только как выход, а не как вход. Если T является только выходом, то никто не сможет поместить тигра в этот аквариум, потому что не существует свойства или метода «поместить в».

Пока мы добавляли эту функцию в C#, я написал несколько статей в блоге; если вас интересуют соображения дизайна, которые вошли в эту функцию, см.:

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

person Eric Lippert    schedule 27.04.2012
comment
Недопустимое отклонение: параметр типа «T» должен быть неизменно действительным для «xx.IContainer‹T›.Contents». «Т» является ковариантным. Я получаю эту ошибку. Я новичок в ковариантных вещах. Что означает ошибка? - person Sandeep; 27.04.2012
comment
@Sandeep: вы каким-то образом используете T в позиции ввода, хотя сказали, что собираетесь использовать ее только в позиции вывода. Является ли Contents свойством с сеттером? Если это так, то очевидно, что T используется во входной позиции, и, следовательно, интерфейс не может быть сделан ковариантным в T. - person Eric Lippert; 27.04.2012
comment
Из всех вопросов, связанных с дисперсией в SO, это, безусловно, лучший ответ, который имеет наибольший смысл. +1 - person Cord Rehn; 20.04.2019

Чтобы исправить код, просто измените

public interface IB<T> where T : IA { }

to

public interface IB<out T> where T : IA { }
person CodingWithSpike    schedule 27.04.2012

Нелегко увидеть, когда у вас есть пустые интерфейсы. Предположим, у вас есть один метод M в интерфейсе IB:

public interface IB<T> where T : IA 
{ 
    void M(T t); 
}

А вот реализация B:

public class B : IB<A>
{
    public void M(A t)
    {
        // only object of type A accepted 
    }
}

Затем у вас есть объект C, который также реализует IA:

public class C : IA { } 

Итак, если бы ваш код был возможен, вы могли бы позвонить:

IB<IA> foo = new B();
foo.M(new C());

Проблема в том, что класс B принимает только объекты типа A. Ошибка!

person Sergey Berezovskiy    schedule 27.04.2012