今天一位同事问我如何将范围添加到集合中。他有一个继承自
Collection<T>
的类。该类型有一个仅获取属性,该属性已包含一些项目。他想要将另一个集合中的项目添加到属性集合中。他如何以 C#3 友好的方式做到这一点? (请注意有关仅获取属性的约束,这会阻止诸如联合和重新分配之类的解决方案。)
当然,一个带有 Property 的 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。这样您就可以利用 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);
在性能方面,它与每个循环相同(语法糖)。
另外不要尝试像这样分配它
var x = source.ForEach(destination.Add)
因为
ForEach
无效。
编辑:从评论中复制,Lippert 对 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 通用集合库类都支持
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);
同意上面一些人和Lipert的意见。 就我而言,经常这样做:
ICollection<int> A;
var B = new List<int> {1,2,3,4,5};
B.ForEach(A.Add);
在我看来,为此类操作提供扩展方法有点多余。
来自
System.Linq
您可以使用
Concat
public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second);
适用于 ICollection、IList、...