同僚が今日、コレクションに範囲を追加する方法を私に尋ねました。彼には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 に類似するものは見つかりませんでした。
悪いデザイン?追加するだけですか?明らかなことを見逃していますか?
いいえ、これは完全に合理的です。 List<T>.
AddRange() 基本的にこれだけを行うメソッドですが、コレクションを具体的にする必要がありますList<T>
。
ループを実行する前に、拡張メソッドでリストにキャストしてみてください。そうすれば、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);
}
}
}
.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
は無効です。
各Add
はコレクションの容量をチェックし、必要に応じて(遅い)サイズを変更することに注意してください。 AddRange
を使用すると、コレクションに容量が設定され、アイテムが追加されます(高速)。この拡張方法は非常に遅くなりますが、機能します。
以下に、もう少し高度な/本番用のバージョンを示します。
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;
}
}
C5 Generic Collections Library クラスはすべてAddRange
メソッドをサポートします。 C5には、基盤となる実装のすべての機能を実際に公開するはるかに堅牢なインターフェイスがあり、System.Collections.Generic
ICollection
およびIList
インターフェイスと互換性があります。つまり、C5
のコレクションは、基礎となる実装として簡単に置き換えることができます。
IEnumerable範囲をリストに追加し、ICollection =をリストに設定できます。
IEnumerable<T> source;
List<item> list = new List<item>();
list.AddRange(source);
ICollection<item> destination = list;
または、次のように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);