web-dev-qa-db-ja.com

コレクションにAddRange

同僚が今日、コレクションに範囲を追加する方法を私に尋ねました。彼にはCollection<T>を継承するクラスがあります。既にいくつかのアイテムを含む、そのタイプの取得専用プロパティがあります。彼は、別のコレクションのアイテムをプロパティコレクションに追加したいと考えています。彼はどのようにしてC#3フレンドリーな方法でそれを行うことができますか? (get-onlyプロパティに関する制約に注意してください。これにより、Unionの実行や再割り当てなどのソリューションが妨げられます。)

確かに、プロパティを持つforeach。追加は機能します。ただし、List<T>スタイルのAddRangeははるかにエレガントです。

拡張メソッドを書くのは簡単です:

public static class CollectionHelpers
{
    public static void AddRange<T>(this ICollection<T> destination,
                                   IEnumerable<T> source)
    {
        foreach (T item in source)
        {
            destination.Add(item);
        }
    }
}

しかし、私は車輪を再発明しているような気がします。 System.Linqまたは morelinq に類似するものは見つかりませんでした。

悪いデザイン?追加するだけですか?明らかなことを見逃していますか?

94
TrueWill

いいえ、これは完全に合理的です。 List<T>.AddRange() 基本的にこれだけを行うメソッドですが、コレクションを具体的にする必要がありますList<T>

55
Reed Copsey

ループを実行する前に、拡張メソッドでリストにキャストしてみてください。そうすれば、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);
        }
    }
}
34
rymdsmurf

.NET4.5ワンライナーが必要な場合 System.Collections.Generic ForEach。

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

またはさらに短く

source.ForEach(destination.Add);

パフォーマンスに関しては、各ループの場合と同じです(構文シュガー)。

また、do n'tのように割り当ててみてください

var x = source.ForEach(destination.Add) 

原因ForEachは無効です。

24

Addはコレクションの容量をチェックし、必要に応じて(遅い)サイズを変更することに注意してください。 AddRangeを使用すると、コレクションに容量が設定され、アイテムが追加されます(高速)。この拡張方法は非常に遅くなりますが、機能します。

19
jvitor83

以下に、もう少し高度な/本番用のバージョンを示します。

    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

C5 Generic Collections Library クラスはすべてAddRangeメソッドをサポートします。 C5には、基盤となる実装のすべての機能を実際に公開するはるかに堅牢なインターフェイスがあり、System.Collections.GenericICollectionおよびIListインターフェイスと互換性があります。つまり、C5のコレクションは、基礎となる実装として簡単に置き換えることができます。

1
Marcus Griep

IEnumerable範囲をリストに追加し、ICollection =をリストに設定できます。

        IEnumerable<T> source;

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

        ICollection<item> destination = list;
0
Jonathan Jansen

または、次のようにICollection拡張を作成できます。

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

        return @this;
    }

リストで使用するのと同じように使用します。

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