将范围添加到集合中

问题描述 投票:0回答:10

今天一位同事问我如何将范围添加到集合中。他有一个继承自

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 中没有找到任何类似的内容。

设计不好?只需调用添加即可?错过了显而易见的事情吗?

c# collections c#-3.0 extension-methods
10个回答
77
投票

不,这看起来完全合理。有一个

List<T>.AddRange()
方法基本上就是这样做的,但要求你的集合是一个具体的
List<T>


46
投票

在运行循环之前尝试在扩展方法中转换为 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);
        }
    }
}

46
投票

因为

.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 的看法


21
投票

请记住,每个

Add
都会检查集合的容量,并在必要时(较慢)调整其大小。使用
AddRange
,集合将设置容量,然后添加项目(更快)。这种扩展方法会非常慢,但会起作用。


7
投票

这是更高级/可用于生产的版本:

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

1
投票

C5 通用集合库类都支持

AddRange
方法。 C5 有一个更强大的接口,实际上公开了其底层实现的所有功能,并且与
System.Collections.Generic
ICollection
IList
接口兼容,这意味着
C5
的集合可以轻松替换为底层实现。


0
投票

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

        IEnumerable<T> source;

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

        ICollection<item> destination = list;

0
投票

或者你可以像这样创建一个 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
投票

同意上面一些人和Lipert的意见。 就我而言,经常这样做:

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

在我看来,为此类操作提供扩展方法有点多余。


0
投票

来自

System.Linq

您可以使用

Concat

public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second);

适用于 ICollection、IList、...

© www.soinside.com 2019 - 2024. All rights reserved.