集合的 AddRange

今天一个同事问我如何向一个集合添加一个范围。他有一个从 Collection<T>继承的类。该类型的 get-only 属性已经包含一些项。他希望将另一个集合中的项添加到属性集合中。他怎么能以 C # 3友好的方式做到这一点呢?(请注意关于 get-only 属性的约束,它阻止执行 Union 和重新分配等解决方案。)

当然,使用 Property. Add 的 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.LinqMorelinq中我没有发现任何类似的东西。

糟糕的设计? 只是调用添加? 忽略了显而易见的东西?

160232 次浏览

不,这看起来很合理。有一个 List<T>.AddRange()方法基本上就是这样做的,但是需要您的集合是一个具体的 List<T>

C5通用收藏图书馆类都支持 AddRange方法。C5有一个更加健壮的接口,它实际上公开了底层实现的所有特性,并且与 System.Collections.Generic ICollectionIList接口兼容,这意味着 C5的集合可以很容易地替换为底层实现。

请记住,每个 Add将检查集合的容量,并在必要时调整其大小(速度较慢)。对于 AddRange,集合将设置容量,然后(更快地)添加项。这种扩展方法将非常缓慢,但是可以工作。

可以将 IEnumable 范围添加到列表中,然后将 ICollection = 设置为列表。

        IEnumerable<T> source;


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


ICollection<item> destination = list;

在运行循环之前,尝试在扩展方法中强制转换为 List。这样您就可以利用 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);

就性能而言,它与每个循环(语法 Sugar)相同。

同样,不要尝试将它分配为

var x = source.ForEach(destination.Add)

因为 ForEach是无效的。

编辑: 拷贝自评论,利伯特对 ForEach 的看法

或者你可以像这样做一个 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);

下面是一个更高级/可生产的版本:

    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;
}
}

同意上面的一些家伙和利伯特的意见。 在我的例子中,通常是这样做的:

ICollection<int> A;
var B = new List<int> {1,2,3,4,5};
B.ForEach(A.Add);

在我看来,这种操作的扩展方法有点多余。