2009-05-04 8 views
1

Si j'ai ces deux classes:Ajout d'une collection d'une sous-classe avec AddRange

class A {} 
class B : A {} 

et je fais une liste <A> mais je veux ajouter une liste <B> à en appelant Liste <A> .AddRange (Liste <B>) mais le compilateur refuse:

Argument '1': cannot convert from 'System.Collections.Generic.List<A>' 
to 'System.Collections.Generic.IEnumerable<B> 

que je comprends tout à fait parce que IEnumerable <B> n'hérite pas de IEnumerable <A>, son type générique a l'héritage.

Ma solution est d'énumérer Liste <B> et ajouter individuellement des articles parce que la liste <A> .Add (Un élément) travaillera avec les éléments B:

foreach(B item in listOfBItems) 
{ 
    listOfAItems.Add(item); 
} 

Cependant, c'est plutôt non-expressive, car ce que je veux c'est juste AddRange.

Je pourrais utiliser

List<B>.ConvertAll<A>(delegate(B item) {return (A)item;}); 

mais c'est inutilement compliqué et mal choisi parce que je ne suis pas la conversion, je suis casting.

Question: Si je devais écrire ma propre liste semblable collection quelle méthode j'ajouter à ce qui me permettrait de copier une collection de B dans une collection d'A comme une doublure semblable à la liste <Un> .AddRange (Liste <B>) et conservent le type maximum de sécurité. (Et par le maximum, je veux dire que l'argument est à la fois une collection et une vérification d'héritage de type.)

Répondre

1

Cela ne fonctionne malheureusement pas car les génériques dans .net ne supportent pas (encore) la covariance.

Vous pouvez toutefois créer une méthode ou une classe d'aide secondaire pour résoudre ce problème.

Si vous implémentez votre propre classe de liste, vous pouvez ajouter covariance à l'aide d'un paramètre générique supplémentaire:

class MyList<T> { 
    void AddRange<U>(IEnumerable<U> items) where U: T { 
     foreach (U item in items) { 
      Add(item); 
     } 
    } 
} 
0

La seule chose que je peux trouver est ce

public class MyList<T> : List<T> 
{ 
    public void AddRange<Tother>(IEnumerable<Tother> col) 
     where Tother: T 
    {  
     foreach (Tother item in col) 
     { 
      this.Add(item); 
     } 
    } 
} 

Appeler cela signifie faire MyList <A> .AddRangeB > (MyListB >). Cela échoue si l'argument n'est pas énumérable ou si l'héritage de type ne fonctionne pas ainsi il satisfait l'exigence de sécurité de type maximum de ma question.

+0

« échoue si l'argument n'est pas dénombrable ou si l'héritage de type ne fonctionne pas. » - Ce comportement est attendu, au moins pour moi. Qu'attendez-vous, ou comment est-ce un problème? – Lucero

+0

C'est exactement ce que je soupçonne. Je disais que cela répond à mes exigences de sécurité maximale de type. –

+0

Aussi (pas sûr si vous l'avez ajouté dans votre édition) la distribution de "article" à "T" ne devrait pas être nécessaire, puisque le compilateur sait (par la contrainte de type générique) que les types sont compatibles. – Lucero

1

Tu ne peux pas juste faire:

listOfAItems.AddRange(listOfBItems.Cast<A>()); 
+0

Euh, je n'ai pas de liste .Cast () méthode. –

+0

Faites-vous référence à System.Linq? Si vous exécutez .NET 2.0, je m'excuse, car Cast est l'une des méthodes d'extension Linq. – BFree

+0

True, et en tant que tel, il est uniquement disponible dans .NET version 3.5 (ou plus récent, quand il sortira). Cependant, alors que les méthodes d'extension sont "propres" dans le code, elles ne sont pas toujours très efficaces. Dans ce cas, je crois que l'approche avec le paramètre générique est beaucoup plus rapide si on l'appelle souvent. – Lucero

1

j'ai pu y parvenir en utilisant LINQ ...

listOfAItems.AddRange(listOfBItems.Cast<A>()); 
+1

S'il vous plaît, essayez de lire ce http://stackoverflow.com/about, pour obtenir plus de compréhension sur les questions/réponses ici sur SO. Votre contribution ne répond pas à la question. C'est plus un commentaire à ** BFree answer **, que vous pouvez ajouter une fois que vous augmenterez votre réputation: http://stackoverflow.com/faq#reputation –

1

Si vous vous trouvez dans une situation où les types génériques ne sont pas variante, la méthode d'extension suivante peut rendre votre vie plus facile:

public static void AddRange<TList,TOther>(this List<TList> list, IEnumerable<TOther> collection) where TOther: TList { 
    foreach(TOther e in collection) { 
     list.Add(e); 
    } 
} 

au lieu d'avoir à tirer de List<T> ou ayant moi cette Dans une classe d'utilitaires, l'utiliser comme une méthode d'extension simplifie l'utilisation. Vous pouvez également profiter de l'inférence, de sorte que cet appel auparavant invalide deviendra valide sans aucune modification:

List<Animal> animals; 
List<Dog> dogs; 
animals.AddRange(dogs); 
Questions connexes