【问题标题】:How to add a range of items to an IList?如何将一系列项目添加到 IList?
【发布时间】:2026-01-19 07:10:01
【问题描述】:

IList<T> 没有 AddRange() 方法。

如何在不遍历项目和使用Add() 方法的情况下将项目列表添加到IList<T>

【问题讨论】:

    标签: c# list collections addrange


    【解决方案1】:

    如果您查看C# source code for List<T>,我认为 List.AddRange() 具有简单循环无法解决的优化问题。因此,扩展方法应该简单地检查 IList 是否为 List,如果是,则使用其原生 AddRange()。

    查看源代码,您会看到 .NET 人员在他们自己的 LINQ 扩展中为 .ToList() 之类的东西做了类似的事情(如果它是一个列表,则强制转换它...否则创建它)。

    public static class IListExtension
    {
        public static void AddRange<T>(this IList<T> list, IEnumerable<T> items)
        {
            if (list == null) throw new ArgumentNullException(nameof(list));
            if (items == null) throw new ArgumentNullException(nameof(items));
    
            if (list is List<T> asList)
            {
                asList.AddRange(items);
            }
            else
            {
                foreach (var item in items)
                {
                    list.Add(item);
                }
            }
        }
    }
    

    【讨论】:

    • 从优化的角度来看,您实际上在这里将list 转换为List&lt;T&gt; 两次。其中之一可以使用 as 关键字进行优化。
    • 好电话@bashis。我总是在双重转换与 GC 清理我们的新 var 的成本上反复讨论。但我们确实可以做 var listCasted = list as List; if(listCasted != null)... 也许 c# 6 声明表达式会改变这种模式:if(myVar.As(out myVarCasted)) myVarCasted...)
    • 你能用提到的优化更新代码吗?我还不太熟悉最新的功能。 @BlackjacketMack
    • @zuckerthoben - 我刚刚运行了一个测试,使用这两种方法迭代了一百万次迭代,性能没有差异。所以我不会称它为优化......此外,它添加了一行代码(但减少了括号强制转换)。无论如何,这些天我可能会使用'as': var listCasted = list as List; if(listCasted != null){listCasted.AddRange(items);}。恕我直言,不值得更新答案,但可以作为语法替代品引入。
    • 现在你可以做if (list is List&lt;T&gt; castedList) { castedList.AddRange(items); }
    【解决方案2】:

    AddRange 是在 List&lt;T&gt; 上定义的,而不是在接口上。

    您可以将变量声明为List&lt;T&gt; 而不是IList&lt;T&gt; 或将其强制转换为List&lt;T&gt; 以获得对AddRange 的访问权限。

    ((List<myType>)myIList).AddRange(anotherList);
    

    这不是一个好的做法(请参阅下面的 cmets),因为 IList&lt;T&gt; 可能不是List&lt;T&gt;,但其他一些实现接口的类型很可能没有 @ 987654333@ 方法 - 在这种情况下,您只会发现代码在运行时抛出异常的时间。

    因此,除非您确定该类型确实是 List&lt;T&gt;,否则您不应尝试使用 AddRange

    这样做的一种方法是使用 isas 运算符测试类型(自 C# 7 起)。

    if(myIList is List<T>)
    {
       // can cast and AddRange
    }
    else
    {
       // iterate with Add
    }
    

    【讨论】:

    • @mohsen.d - 如果生成了类型,您不想更改生成的代码(因为它很容易被覆盖)。转换或使用 LINQ Concat,作为 @Self_Taught_Programmer answered
    • @mohsen.d - 如果是您的代码,不妨将类型声明为List&lt;T&gt;(或者,如果这对您来说不是一个好的选择,请在您需要的地方进行转换@ 987654339@ 保持本地化 - 这是一个成本非常低的操作)。
    • 不,不,不。这是一个 IList 开始的原因是因为它可能不是 List 实现。如果确实需要 AddRange 方法,请编写 BlackjacketMack 所示的扩展方法。
    • 不知道为什么它会收到如此多的赞成票,因为如果用于List&lt;T&gt; 以外的任何东西(例如数组),它显然会抛出类似InvalidCastException 的东西。
    • 但是,强制转换不是一个好主意。这可能会导致性能开销。
    【解决方案3】:

    你可以这样做:

    IList<string> oIList1 = new List<string>{"1","2","3"};
    IList<string> oIList2 = new List<string>{"4","5","6"};
    IList<string> oIList3 = oIList1.Concat(oIList2).ToList();
    

    所以,基本上你会使用Concat() 扩展和ToList() 来获得与AddRange() 类似的功能。

    Source

    【讨论】:

    • 您的方法的问题是Enumerable.Concat 是由System.Linq.Enumerable 实现的,并且该方法的返回值是IEnumerable&lt;TSource&gt;,所以我认为它不应该被转换回IList&lt;TSource&gt; - 它由于不检查源代码我们不知道的实现细节,可能会返回其他内容 - 尽管不能保证它不会改变 - 因此在支持多个 .NET 版本时必须特别注意。
    【解决方案4】:

    你也可以这样写一个扩展方法:

    internal static class EnumerableHelpers
    {
        public static void AddRange<T>(this IList<T> collection, IEnumerable<T> items)
        {
            foreach (var item in items)
            {
                collection.Add(item);
            }
        }
    }
    

    用法:

    IList<int> collection = new MyCustomList(); //Or any other IList except for a fixed-size collection like an array
    var items = new[] {1, 4, 5, 6, 7};
    collection.AddRange(items);
    

    它仍在迭代项目,但您不必每次调用它时都编写迭代或强制转换。

    【讨论】:

      【解决方案5】:

      另一个使用 LINQ 的答案,前提是您要添加的是 List&lt;T&gt; 或者您可以在其上调用 ToList()

      IEnumerable<string> toAdd = new string[] {"a", "b", "c"};
      IList<string> target = new List<string>();
      
      toAdd.ToList().ForEach(target.Add);
      

      【讨论】:

        【解决方案6】:

        IList 没有 AddRange() ,但有 Concat() 结合你的收藏

        【讨论】:

        • 最好加个代码示例,让你回答更有用