2016-06-09 4 views
2

У меня есть следующий шаблон интерфейса класса, на котором я хочу использовать MEF экспорта и экспорта:Почему следующий шаблон импорта/экспорта с интерфейсами не является законным?

public interface IDinosaur 
{ 
    string Species { get; } 
} 

public class Pterodactyl : IDinosaur 
{ 
    public string Species { get; set; } 
    public float WingSpan { get; set; } 
} 

public interface ICage<in T> 
{ 
    void Transport(T animal); 
} 

[Export(typeof(ICage<IDinosaur>))] // <-- This appears a problem 
public class PterodactylCage : ICage<Pterodactyl> 
{ 
    public void Transport(Pterodactyl dinosaur) { } 
} 

public class DinoTransportationService 
{ 
    [Import(AllowDefault = true)] 
    private ICage<IDinosaur> m_dinosaurCage = null; 
} 

Теперь я хотел бы сказать, что это является законным, так как [Export(typeof(ICage<IDinosaur>))] indeeds экспортирует ICage в виде IDinosaur (который, случается, a Pterodactyl, но это реализует интерфейс динозавров, чтобы это было правильно?). Однако это дает мне исключение CompositionException. говоря:

"The export 'PterodactylCage (ContractName=\"ICage(IDinosaur)\")' is not assignable to type 'ICage`1[[IDinosaur, MyProgramme, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'." 

Я мог бы это исправить, изменив экспорт в:

[Export(typeof(ICage<Pterodactyl>))] // <-- This is ok 
public class PterodactylCage : ICage<Pterodactyl> 

И импорт в:

[Import(AllowDefault = true)] 
private ICage<Pterodactyl> m_dinosaurCage = null; 

Кроме этого, что импорт теперь становится слишком конкретным, и в факт, в моем случае импорт находится в другой сборке, которая никогда не слышала о Pterodactyls, так что это плохое решение. Почему первый пример не является законным, и каково решение для этого шаблона?

ответ

2

Это недопустимо, так как (как сообщение об ошибке говорит, кстати) экземпляр PterodactylCage не присваиваемые ICage<IDinosaur>:

ICage<IDinosaur> cage = new PterodactylCage(); // this won't compile 

Это связано с ковариационной \ контрвариации общих интерфейсов, вы можете прочитать, что это во многих местах через Интернет (вот широкая тема для описания здесь).

Если у вас это определение интерфейса:

public interface ICage<out T> // note "out" keyword 

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

Немного больше о том, почему это незаконно интуитивно. Предположим, что это законно. Тогда у вас есть ICage<IDinosaur> экземпляров с Tranport метода который принимает IDinosaur. Но базовый тип - PterodactylCage, который является ICage<Pterodactyl>. Но вы можете передать любой пример IDinosaur методу Transport, а не только Pterodactyl (помните - мы работаем с ICage<IDinosaur>), что приводит нас к противоречию. Вот почему интуитивно это не законно.

+0

Спасибо, это объяснение действительно является хорошим объяснением того, почему вышеуказанное не является законным. Есть ли альтернатива, которая делает то, что я хочу сделать возможным? – Yellow

+0

Well ICage НЕ МОЛОДЕЖЬ в вашем случае. Если вы каким-то образом сможете добиться успеха в своем экспорте, а кто-то передал Theropods в ваш метод транспорта - он будет терпеть неудачу во время выполнения. Поэтому вы должны пересмотреть свою логику, потому что то, что вы пытаетесь сделать, просто неверно. – Evk

+0

Но вы можете использовать ICage вместо ICage и экспортировать его. Вы можете проверить внутри метода транспорта, какой объект был передан как IDinosaur здесь (потому что это может быть любой динозавр снова). – Evk