AddRange到集合

Tru*_*ill 97 c# collections extension-methods c#-3.0

一位同事今天问我如何为一个系列添加一个范围.他有一个继承自的课程Collection<T>.这种类型的get-only属性已包含一些项目.他想将另一个集合中的项目添加到属性集合中.他怎么能以C#3友好的方式这样做呢?(注意关于get-only属性的约束,这会阻止像执行Union和重新分配这样的解决方案.)

当然,与物业的foreach.添加将工作.但是A 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但我感觉我正在重新发明轮子.我没有发现任何类似System.Linqmorelinq.

糟糕的设计?只需致电添加?缺少明显的?

Ree*_*sey 57

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

  • 是的 - 我更多地理解为什么我不认为这样做有问题.只是意识到它的效率低于List <T>版本(因为列表<T>可以预先分配) (6认同)

rym*_*urf 34

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

  • 实际上我是认真的.主要原因是它是额外的认知负荷,这通常非常困难.你一直在试图评估负面情况,这通常是相对困难的,无论如何你都有两个分支,它(IMO)更容易说'if null'这样做,'else'这样做,而不是相反.它也是关于默认值,它们应该是尽可能经常的积极概念,.eg`if(!thing.IsDisabled){} else {}'要求你停下来思考'啊,不是禁用意味着启用,对,得到了,所以另一个分支就是当它被禁用时).难以解析. (12认同)
  • 解释"something!= null"并不比解释"something == null"困难.然而,否定运算符是完全不同的东西,在你的最后一个例子中,重写if-else语句将_elliminate_该运算符.这是客观上的改进,但与原始问题无关.在这种特殊情况下,这两种形式是个人偏好的问题,我更喜欢"!=" - 运算符,给出上述推理. (12认同)
  • 模式匹配会让每个人都开心...... ;-)`if(destination is List <T> list)` (11认同)
  • arrgggh!交换条件分支,为所有圣洁的爱! (4认同)
  • @nicodemus13,(假设你至少有点认真)你交换它们的原因是什么?首先考虑作为我们进行检查的唯一原因的情况不是更自然吗? (3认同)
  • `as`操作符永远不会抛出.如果`destination`不能被强制转换,`list`将为null,并且`else`块将被执行. (2认同)

Mat*_*ius 24

因为.NET4.5如果你想要单行,你可以使用System.Collections.GenericForEach.

source.ForEach(o => destination.Add(o));
Run Code Online (Sandbox Code Playgroud)

甚至更短

source.ForEach(destination.Add);
Run Code Online (Sandbox Code Playgroud)

性能方面与每个循环(语法糖)相同.

不要尝试分配它

var x = source.ForEach(destination.Add) 
Run Code Online (Sandbox Code Playgroud)

原因ForEach是无效的.

  • 就我个人而言,我和Lippert在一起:http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx (9认同)
  • `ForEach`似乎只在`List <T>`上定义,而不是`Collection`? (4认同)
  • 现在可以在 https://web.archive.org/web/20190316010649/https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/ 找到 Lippert (2认同)
  • 更新了 Eric Lippert 博客文章的链接:[编码中的精彩冒险 | “foreach”与“ForEach”](https://docs.microsoft.com/en-us/archive/blogs/ericlippert/foreach-vs-foreach) (2认同)

jvi*_*r83 19

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

  • 除此之外,每次添加都会有一个集合更改通知,而不是使用AddRange进行一次批量通知. (3认同)

Mov*_*GP0 5

这是更高级/生产就绪的版本:

    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;
        }
    }
Run Code Online (Sandbox Code Playgroud)