2009-02-25 5 views
5

J'ai une routineC# .NET en passant une collection d'objets InterfaceImplementingClass à une routine qui prend une collection d'objets Interface

public void SomeRoutine(List<IFormattable> list) { ... } 

J'essaie alors d'appeler cette routine

List<Guid>list = new List<Guid>(); 
list.Add(Guid.NewGuid()); 
SomeRoutine(list); 

Et il échoue avec une erreur de compilation. System.Guid implémente IFormattable, mais l'erreur que je reçois est

ne peut pas convertir 'System.Collections.Generic.List' à 'System.Collections.Generic.List'

NOTE : Vous obtiendrez la même erreur si vous utilisez simplement un tableau de Guids. Génériques n'est pas la cause ....

Mais! Compte tenu de cette

public void SomeRoutine2(IFormattable obj) { ... } 

et ce

Guid a = Guid.NewGuid(); 
SomeRoutine2(a); 

Il compile! Donc la question est pourquoi? Pourquoi suis-je capable de passer un objet Guid (qui implémente IFormattable) dans une routine qui accepte un objet de IFormattable, mais quand j'essaye de le développer en une collection (une liste générique, ou un tableau, ou n'importe quoi d'autre), je reçois une erreur de conversion?

J'ai eu beaucoup de temps à trouver une réponse, et j'ai pensé que ce serait le meilleur endroit où aller.

+0

Vous vous rendez compte que la réponse marquée (covariance .NET 4.0) ne s'applique pas réellement aux listes, où-comme génériques fonctionne * maintenant *? –

+0

Voir aussi: http://marcgravell.blogspot.com/2009/02/what-c-40-covariance-doesn-do.html –

+0

Sauf que cela n'a pas fonctionné avec un tableau de Guids avec une routine qui cherche un tableau de IFormattable, soit ....? – emkayultra

Répondre

8
+0

La covariance C# 4.0 ne s'applique pas aux classes concrètes (uniquement aux interfaces) et ne s'applique pas aux listes (seulement IEnumerable - ou aux objets qui sont purement "out") –

+0

Que voulez-vous dire exactement quand vous dites "pure out" "? – BFree

+0

Je veux dire, le 'IEnumerable ' et 'IEnumerator ' ont seulement une utilisation "out" - c'est-à-dire "T Current {get;}". C'est marquer les choses comme "in" ou "out" qui permet co/contra-variance; mais cela ne fonctionne qu'avec un ** ou ** l'autre. Vous ne pouvez pas utiliser "in" et "out" (quelles listes nécessiteraient). –

2

Avez-vous essayé de déclarer list comme un objet List<IFormattable> au lieu d'un List<Guid>? Cela devrait vous permettre de dépasser l'erreur de compilation.

+0

Les génériques sont une option beaucoup plus agréable ... –

3

Le problème est qu'une liste <IFormattable> est non seulement une collection à partir de laquelle vous pouvez lire quelques IFormattables, il est aussi une collection à laquelle vous pouvez ajouter IFormattables. Une liste <Guid> répond à la première exigence, mais pas la seconde. Que faire si SomeRoutine était

public void SomeRoutine(List<IFormattable> list) 
{ 
    list.Add(5); 
} 

Ce Int32 est un IFormattable, la méthode devrait pouvoir l'ajouter à la liste <IFormattable> qu'il a demandé. Cela ne fonctionnera pas si le compilateur vous laisse passer dans votre liste <Guid>.

Les nouvelles fonctionnalités de C# 4.0 auxquelles BFree se réfère vous permettront de dire au compilateur quand ces choses sont sûres.Vous pouvez dire soit

  • Bien que j'ai demandé une référence IFormattable, je la lirai seulement. Si c'est vraiment une référence Guid alors je peux le traiter comme un IFormattable, et je ne vais pas essayer de lui assigner un Int32.
  • Bien que j'ai demandé une référence IFormattable, je ne ferai qu'écrire dessus. Si c'est vraiment une référence d'objet alors je peux assigner mon IFormattable en toute sécurité, et peu importe si la valeur courante n'est pas IFormattable parce que je ne la lirai pas.
+0

C# 4.0 covariance doesn ' t s'appliquent aux classes concrètes (seulement aux interfaces), et ne s'applique pas aux listes (seulement IEnumerable - ou les choses qui sont "out" –

+0

correction, interfaces + délégués, mais les génériques est toujours une meilleure réponse ici ... –

+0

Bon point: bien sûr, une liste ne peut faire aucune des affirmations 'in' ou 'out' que C# 4.0 autorisera parce que vous devez toujours pouvoir insérer et récupérer des T, donc vous n'êtes pas mieux lotis. +1 pour vos génériques. – stevemegson

5

Ceci est un cas d'utilisation de génériques classique; essayez:

public static void SomeRoutine<T>(IList<T> list) where T : IFormattable 
{ ... } 

maintenant à l'intérieur SomeRoutine vous avez accès à tous les IFormattable membres, mais il fonctionnera avec:

List<Guid>list; ... 
SomeRoutine(list); // note no need to specify T 

Edit: J'ai plus blogged sur les différences entre 4,0 covariance et génériques pour ce scénario.

Questions connexes