Хорошо, давайте заменим 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