2009-08-14 2 views
1

Possible en double:
Upcasting and generic listsQuelques problèmes avec l'envoi de la liste <T> comme IEnumerable <T> à une méthode

Ok, je veux envoyer un List<CardHolder> comme IEnumerable<ICardHolder>CardHolder : ICardHolder. Cependant, les erreurs du compilateur:

erreur 4 Argument '1': ne peut pas convertir 'System.Collections.Generic.List' à 'System.Collections.Generic.IEnumerable'

Cela semble étrange pour moi, compte tenu d'un List<T> : IEnumerable<T>. Qu'est-ce qui ne va pas?

public interface ICardHolder 
{ 
    List<Card> Cards { get; set; } 
} 

public class CardHolder : ICardHolder 
{ 
    private List<Card> cards = new List<Card>(); 
    public List<Card> Cards 
    { 
     get { return cards; } 
     set { cards = value; } 
    } 

    // ........ 
} 

public class Deck : ICardHolder 
{ 
    // ......... 

    public void Deal(IEnumerable<ICardHolder> cardHolders) 
    { 
     // ........ 
    } 

    // ......... 
} 

public class Game 
{ 
    Deck deck = new Deck(); 
    List<CardHolder> players = new List<CardHolder>(); 

    // ......... 

    deck.Deal(players); // Problem is here! 

    // ......... 
} 

Répondre

7

Le problème est que List<T> est pas un sous-type de IEnumerable<T1> même si T : T1.

Les génériques en C# (avant C# 4.0) sont 'invariants' (c'est-à-dire qu'ils n'ont pas cette relation de sous-typage). Dans .Net 4, IEnumerable<T> aura son paramètre de type annoté comme étant 'covariant'. Cela signifie que List<T> sera un sous-type de IEnumerable<T1> si T : T1. Pour plus de détails sur cette fonctionnalité, consultez this page sur MSDN.

Modifier - Vous pouvez contourner ce dans votre cas en faisant le générique méthode Deal:

public void Deal<T>(IEnumerable<T> cardHolders) where T : ICardHolder 
{ 
    // ........ 
} 
+0

OK, donc je dois convertir ma liste Liste de pouvoir l'envoyer en IEnumerable? Oh, et est-il possible d'avoir une sorte d'aperçu pour C# 4.0 sur VS2008? –

+1

Il ya un excellent article InfoQ sur la co/contravariance dans les génériques ici: http://www.infoq.com/news/2008/08/GenericVariance –

+0

+50 pour la méthode générique (si je pouvais). Je vous remercie! –

0

Ajout à la réponse de Ben, vous devez remplacer la ligne:

List<CardHolder> players = new List<CardHolder>(); 

avec:

List<ICardHolder> players = new List<ICardHolder>(); 

Vous êtes mieux avec des interfaces de toute façon ... :)

+0

Malheureusement, cela ne fonctionne pas avec le reste du code que j'ai, car il a besoin d'accéder à certaines propriétés spécifiques de CardHolder qu'ICardHolder n'a pas (et ne peut pas) avoir. Mais merci quand même. –

+0

pour les propriétés spécifiques de CardHolder, vous pouvez toujours convertir vers CardHolder ... –

1

Un List<CardHolder> est un IEnumerable<CardHolder>, mais un IEnumerable<CardHolder> n'est pas un IEnumerable<ICardHolder>. Les deux interfaces ne sont pas liées (sauf pour leur structure).

C# 4.0 introduit covariance et contravariance, qui peut résoudre ce genre de problème

Pendant ce temps, en C# 3.0, vous pouvez faire:

deck.Deal(players.Cast<ICardHolder>()); 

Il ne devrait avoir aucun impact significatif sur la performance, puisque la collection n'est recensée une fois, et le upcast à ICardHolder est un no-op en MSIL

Questions connexes