Un collègue m'a demandé aujourd'hui comment ajouter une gamme à une collection. Il a une classe qui hérite de Collection<T>
. Il existe une propriété get-only de ce type qui contient déjà certains éléments. Il souhaite ajouter les éléments d'une autre collection à la collection de propriétés. Comment peut-il le faire de manière conviviale C # 3? (Notez la contrainte concernant la propriété get-only, qui empêche des solutions telles que faire Union et réaffecter.)
Bien sûr, un foreach avec la propriété. L'ajout fonctionnera. Mais un List<T>
style AddRange serait beaucoup plus élégant.
Il est assez facile d'écrire une méthode d'extension:
public static class CollectionHelpers
{
public static void AddRange<T>(this ICollection<T> destination,
IEnumerable<T> source)
{
foreach (T item in source)
{
destination.Add(item);
}
}
}
Mais j'ai le sentiment de réinventer la roue. Je n'ai rien trouvé de similaire dans System.Linq
ou morelinq .
Mauvaise conception? Appelez simplement Ajouter? Vous manquez l'évidence?
la source
ICollection<T>
ne semble pas avoir deAdd
méthode. msdn.microsoft.com/en-us/library/... Cependant enCollection<T>
a un.Add(T item)
en premier lieu? Cela ressemble à une approche à moitié cuite pour offrir la possibilité d'ajouter un seul élément, puis de s'attendre à ce que tous les appelants itèrent pour en ajouter plusieurs à la fois. Votre déclaration est certainement vraie,IEnumerable<T>
mais je me suis retrouvé frustré àICollections
plus d'une occasion. Je ne suis pas en désaccord avec vous.Réponses:
Non, cela semble parfaitement raisonnable. Il existe une méthode
List<T>.
AddRange () qui fait exactement cela, mais nécessite que votre collection soit un bétonList<T>
.la source
Essayez de lancer un cast en List dans la méthode d'extension avant d'exécuter la boucle. De cette façon, vous pouvez profiter des performances de List.AddRange.
la source
as
opérateur ne lancera jamais. Sidestination
ne peut pas être casté,list
sera nul et leelse
bloc s'exécutera.if (destination is List<T> list)
Puisque
.NET4.5
si vous voulez une seule ligne, vous pouvez utiliserSystem.Collections.Generic
ForEach.ou même plus court que
En termes de performances, c'est la même chose que pour chaque boucle (sucre syntaxique).
Aussi n'essayez d' affecter comme
la cause
ForEach
est nulle.Edit: Copié à partir des commentaires, l'opinion de Lipert sur ForEach
la source
ForEach
semble être défini uniquement surList<T>
, nonCollection
?N'oubliez pas que chacun
Add
vérifiera la capacité de la collection et la redimensionnera chaque fois que nécessaire (plus lentement). AvecAddRange
, la collection sera définie la capacité, puis ajouté les éléments (plus rapide). Cette méthode d'extension sera extrêmement lente, mais fonctionnera.la source
Voici une version un peu plus avancée / prête pour la production:
la source
destination
est une liste deShape
, une classe abstraite.source
est une liste deCircle
, une classe héritée.Les classes de la bibliothèque de collections génériques C5 prennent toutes en charge la
AddRange
méthode. C5 a une interface beaucoup plus robuste qui expose en fait toutes les fonctionnalités de ses implémentations sous-jacentes et est compatible avec les interfacesSystem.Collections.Generic
ICollection
etIList
, ce qui signifie queC5
les collections de s peuvent être facilement remplacées comme implémentation sous-jacente.la source
Vous pouvez ajouter votre plage IEnumerable à une liste, puis définir ICollection = sur la liste.
la source
Ou vous pouvez simplement créer une extension ICollection comme ceci:
L'utiliser serait comme l'utiliser sur une liste:
la source