web-dev-qa-db-fra.com

Ajouter une collection à une collection

Un collègue m'a demandé aujourd'hui comment ajouter une plage à 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 d'une manière conviviale en C # 3? (Notez la contrainte relative à la propriété get-only, qui empêche des solutions telles que l'Union et la réaffectation.)

Bien sûr, une foreach avec la propriété. Ajouter fonctionnera. Mais un List<T>- style AddRange serait beaucoup plus élégant.

C'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 semblable dans System.Linq ou morelinq .

Mauvais design? Il suffit d'appeler Ajouter? Manquer l'évidence?

85
TrueWill

Non, cela semble parfaitement raisonnable. Il existe une méthode List<T>.AddRange () qui fait essentiellement cela, mais nécessite que votre collection soit un List<T> concret.

52
Reed Copsey

Essayez de transtyper List dans la méthode d'extension avant d'exécuter la boucle. De cette façon, vous pouvez tirer parti des performances de List.AddRange.

public static void AddRange<T>(this ICollection<T> destination,
                               IEnumerable<T> source)
{
    List<T> list = destination as List<T>;

    if (list != null)
    {
        list.AddRange(source);
    }
    else
    {
        foreach (T item in source)
        {
            destination.Add(item);
        }
    }
}
31
rymdsmurf

Depuis .NET4.5 si vous voulez une ligne, vous pouvez utiliser System.Collections.Generic ForEach.

source.ForEach(o => destination.Add(o));

ou même plus court que

source.ForEach(destination.Add);

En termes de performances, c'est la même chose que pour chaque boucle (sucre syntaxique).

Aussi ne pas essayer de l'assigner comme

var x = source.ForEach(destination.Add) 

cause ForEach est nul.

23
Matas Vaitkevicius

N'oubliez pas que chaque Add vérifiera la capacité de la collection et la redimensionnera si nécessaire (plus lentement). Avec AddRange, la collection sera définie la capacité, puis ajoutée aux éléments (plus rapidement). Cette méthode d'extension sera extrêmement lente, mais fonctionnera.

19
jvitor83

Voici une version un peu plus avancée/prête pour la production:

    public static class CollectionExtensions
    {
        public static TCol AddRange<TCol, TItem>(this TCol destination, IEnumerable<TItem> source)
            where TCol : ICollection<TItem>
        {
            if(destination == null) throw new ArgumentNullException(nameof(destination));
            if(source == null) throw new ArgumentNullException(nameof(source));

            // don't cast to IList to prevent recursion
            if (destination is List<TItem> list)
            {
                list.AddRange(source);
                return destination;
            }

            foreach (var item in source)
            {
                destination.Add(item);
            }

            return destination;
        }
    }
2
MovGP0

Les classes C5 Generic Collections Library supportent toutes la méthode AddRange. C5 a une interface beaucoup plus robuste qui expose en réalité toutes les fonctionnalités de ses implémentations sous-jacentes et est compatible avec les interfaces System.Collections.GenericICollection et IList, ce qui signifie que les collections de C5 peuvent facilement être substituées à l'implémentation sous-jacente.

1
Marcus Griep

Vous pouvez ajouter votre plage IEnumerable à une liste, puis définir ICollection = sur la liste.

        IEnumerable<T> source;

        List<item> list = new List<item>();
        list.AddRange(source);

        ICollection<item> destination = list;
0
Jonathan Jansen

Ou vous pouvez simplement créer une extension ICollection comme ceci:

 public static ICollection<T> AddRange<T>(this ICollection<T> @this, IEnumerable<T> items)
    {
        foreach(var item in items)
        {
            @this.Add(item);
        }

        return @this;
    }

L’utiliser serait comme l’utiliser sur une liste: 

collectionA.AddRange(IEnumerable<object> items);
0
Katarina Kelam