2009-09-23 10 views
4

Ma question concerne les classes de diffusion à l'intérieur d'un type générique. Bien que je sache que lancer des objets comme List<string> à List<object> nécessite la prise en charge de la covariance pour éviter d'ajouter des objets object à une liste contenant des chaînes, je me demande pourquoi une distribution comme ci-dessous n'est pas acceptée par le compilateur et si elle peut être résolue en utilisant des interfaces. ou contravariance:Génériques, héritage et diffusion

public class TypeA<T> {} 

public class TypeB<T> {} 

public class TypeC : TypeB<int> {} 

class Program 
{ 
    public static void MyMethod<OutputType>(TypeA<TypeB<OutputType>> Parameter) {} 

    static void Main(string[] args) 
    { 
     TypeA<TypeC> Test = new TypeA<TypeC>(); 
     MyMethod<int>(Test); 
    } 
} 

Compiler cela se traduit par une erreur:

Argument 1: cannot convert from 'ConsoleApplication1.TypeA<ConsoleApplication1.TypeC>' to 'ConsoleApplication1.TypeA<ConsoleApplication1.TypeB<int>>'.

même si TypeC est un descendant direct de TypeB<int>

Répondre

2

Eh bien, puisque C# 4.0 prend en charge covariance pour les interfaces avec les génériques, l'exemple suivant peut éventuellement résoudre le problème:

public interface ITypeA<out T> {} 

public class TypeA<T> : ITypeA<T> {} 

public class TypeB<T> {} 

public class TypeC : TypeB<int> {} 

class Program 
{ 
    public static void MyMethod<OutputType>(ITypeA<TypeB<OutputType>> Parameter) {} 

    static void Main(string[] args) 
    { 
     ITypeA<TypeC> Test = new TypeA<TypeC>(); 
     MyMethod<int>(Test); 
    } 
} 

Notez que le type T ne peut être utilisé que pour les valeurs renvoyées par les méthodes de ITypeA.

+0

Il s'avère que c'était un manque de compréhension de ma part sur la façon dont la covariance fonctionne avec les interfaces. Comme Eric le souligne dans sa réponse, TypeB et TypeC sont analogues à la casse de liste. Une solution similaire résout en fait mon problème spécifique, merci beaucoup. –

0

Mais ConsoleApplication1.TypeA<ConsoleApplication1.TypeC> ne possèdes pas de ConsoleApplication1.TypeA<ConsoleApplication1.TypeB<int>>

0

Disons que je j'ai un objet de la liste. Je l'ai ensuite jeté à la liste. La liste a une méthode appelée 'add' qui prend un argument T. J'y ajoute un Apple (puisque j'ai une liste). Cependant, dans certains endroits, mon code attend encore List, donc certains membres de List sont en fait des pommes !!!

Ainsi, TypeA ne peut pas être jeté à TypeA même si X hérite de Y.

+0

Comment savez-vous qu'un Apple est de type T? – recursive

+0

Et qu'est-ce que X et Y? En fait, je ne comprends rien de tout cela. – recursive

+0

Il était censé être un exemple de pourquoi l'héritage était une mauvaise idée. Vous ne pouvez pas lancer la liste à la liste , tout comme vous ne pouvez pas lancer la liste (quel que soit X) à la liste même si X étend Y. Dans ce cas, T était un type générique, et j'utilisais un Liste . – luiscubal

2

Un autre problème générique de covariance - voir la réponse que j'ai posté here. TypeA <TypeC> ne hérite pas de TypeA < TypeB <int> > de quelque manière que

3

Comme d'autres commentateurs l'ont souligné, en dépit du fait que TypeC est dérivé de TypeB<int>, il est pas vrai que TypeA<TypeC> dérive de TypeA<TypeB<int>>. Cependant, vous pouvez probablement obtenir votre code de travail en ajoutant un paramètre de type supplémentaire MyMethod:

public static void MyMethod<DerivedB,OutputType>(TypeA<DerivedB> Parameter) 
    where DerivedB : TypeB<OutputType> {} 
3

Je suis plutôt confus par votre question, étant donné que le préambule de la question montre que vous savez ce que la réponse est. Vous ne pouvez pas convertir un List<string> en un List<object> parce que vous pourriez alors ajouter une girafe à la liste des objets qui est vraiment une liste de chaînes, ce qui n'est pas de type sécurisé. Si vous remplacez "TypeA" par "List", TypeB<int> avec "object" et "TypeC" avec "string" alors vous avez transformé votre situation en celle que vous savez déjà ne fonctionne pas pour de bonnes raisons. Ce n'est pas que le compilateur connaisse magiquement quelque chose à propos de List et n'autorise pas le scénario List - plutôt, le compilateur n'autorise pas une telle variance. Dans C# 4, nous ajoutons la variance sur certains types d'interface et de délégué qui sont connus pour être de type safeafe sous variance. C'est un modèle "opt-in" - vous devez prouver au compilateur que vous êtes en sécurité, et seulement alors nous vous laisserons utiliser la variance.