2012-04-27 3 views
14

je les classes/interfaces suivantes:Implémentation d'interfaces génériques imbriqués

// 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 { } 

je tente de créer une nouvelle instance en utilisant le code suivant:

IB<IA> foo = new B(); 

Je reçois l'erreur suivante:

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

Quelqu'un peut-il expliquer pourquoi ce n'est pas possible?

+0

Quelle version de C# utilisez-vous? – Oded

+3

[FAQ sur la covariance et la contravariance] (http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx) et [cette série de blog] (http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/) par Eric Lippert – Oded

+1

Répondre

41

OK, remplaçons A avec Fish, IA avec IAnimal, B avec Aquarium et IB<T> avec IContainer<T>. Et nous allons ajouter un membre à IContainer<T>, et une deuxième mise en œuvre de 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! 

Vous pouvez mettre un tigre dans foo - foo est entré comme un récipient qui peut contenir tout animal. Mais vous pouvez seulement mettre un poisson dans un aquarium. Étant donné que les opérations que vous pouvez effectuer légalement sur un Aquarium sont différent que les opérations que vous pouvez effectuer sur un IContainer<IAnimal>, les types ne sont pas compatibles.

La fonction que vous voulez est appelé covariance d'interface générique et est soutenu par C# 4, mais vous devez prouver au compilateur que vous ne serez jamais mettre un tigre dans votre aquarium. Qu'est-ce que vous voulez faire est:

// 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 { } 

Notez l'annotation de covariance sur IB. Ce out signifie que T peut uniquement être utilisé comme sortie, et non comme une entrée. Si T est seulement une sortie alors il n'y a aucun moyen pour quelqu'un de mettre un tigre dans ce aquarium parce qu'il n'y a pas de "mettre en" propriété ou méthode possible.

J'ai écrit un certain nombre d'articles de blog pendant que nous ajoutions cette fonctionnalité à C#; si vous êtes intéressé par les considérations de conception qui est entré dans la fonction, voir:

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

+0

Variance invalide: Le paramètre de type 'T' doit être invariant valide sur 'xx.IContainer .Contents'. 'T' est covariant. \t Je reçois cette erreur. Je suis nouveau pour les choses covariantes. Que signifie l'erreur? – Sandeep

+3

@Sandeep: Vous utilisez en quelque sorte T dans une position d'entrée lorsque vous avez dit que vous ne l'utiliserez que dans une position de sortie. Est-ce que 'Contents' est une propriété avec un setter? Si c'est alors clairement que T est utilisé dans une position d'entrée, et donc l'interface ne peut pas être rendue covariante dans T. –

+0

Merci Eric. Cela l'a résolu. – Sandeep

1

Pour corriger votre code, il suffit de changer

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

à

public interface IB<out T> where T : IA { } 
+0

awww, Eric m'a battu :) – CodingWithSpike

0

Pas facile pour voir quand vous avez des interfaces vides.Considérez-vous une méthode M dans l'interface IB:

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

Et est ici mise en œuvre de B:

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

Ensuite, vous avez l'objet C, qui met également en œuvre IA:

public class C : IA { } 

Alors , si votre code est possible, alors vous pouvez appeler:

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

Le problème est que la classe B n'accepte que les objets de type A. Erreur!

Questions connexes