如何将一系列项添加到IList变量


Answers:


65

AddRange在上定义List<T>,而不是在接口上定义。

您可以将变量声明为,List<T>而不是IList<T>或将其List<T>强制转换为访问AddRange

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

这是不好的做法(见下面的评论),作为一个IList<T>实力List<T>,但实现的接口,并很可能不会有一些其他类型的AddRange方法-在这种情况下,你只会发现当你的代码引发运行时异常。

因此,除非您确定该类型确实是a,否则List<T>不应尝试使用AddRange

一种方法是通过使用isas运算符测试类型(自C#7起)。

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

2
@ mohsen.d-如果生成了类型,则您不想更改生成的代码(因为它可能会被覆盖)。Concat就像@Self_Taught_Programmer回答的那样,强制转换或使用LINQ 。
奥德

2
@ mohsen.d-如果是您的代码,则可以将类型声明为List<T>(或者,如果这对您而言不是一个不错的选择,请在需要将AddRange其保持本地化的位置进行强制转换-这是一种非常低成本的操作)。
2012年

53
不不不。之所以以IList <T>开头,是因为它可能不是List <T>实现。如果您确实需要AddRange方法,请编写一个BlackjacketMack所示的扩展方法。
Derek Greer

6
不知道为什么这会收到如此多的赞誉,因为它显然会抛出类似InvalidCastException在非List<T>数组(例如数组)上使用的东西。
bashis

3
但是,铸造不是一个好主意。这可能会导致性能开销。
Gul Md Ershad

77

如果您看一下Listc#源代码,我认为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);
            }
        }
    }
}

5
从优化的角度来看,您实际上是在这里list进行List<T>两次铸造。其中之一可以用as关键字优化。
bashis

好的@bashis。我总是来回比较我们新变量的两次强制转换与GC清理的成本。但是我们确实可以做var listCasted = list as List <T>; if(listCasted!= null)...也许c#6声明表达式将更改此模式:if(myVar.As(out myVarCasted))myVarCasted ...)
BlackjacketMack

您能否使用上述优化来更新代码?我对最新功能还不太熟练。@BlackjacketMack

2
@zuckerthoben-我只是运行了一个测试,使用这两种方法迭代了一百万次迭代,并且性能没有差异。因此,我不会将其称为优化...此外,它还会添加一行代码(但会减少parens强制转换)。无论如何,这些天我可能会使用'as':var listCasted = list as List <T>; if(listCasted!= null){listCasted.AddRange(items);}。不值得更新答案恕我直言,但可以作为一种语法替代方法进行介绍。
BlackjacketMack

2
截至目前,你可以做if (list is List<T> castedList) { castedList.AddRange(items); }
安德烈Mantas公司

23

您可以执行以下操作:

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()

资源


1
您的方法存在的问题是,该方法Enumerable.Concat是由实现的,System.Linq.Enumerable并且该方法的返回值是IEnumerable<TSource>,因此我认为不应将其强制转换为IList<TSource>-由于我们不了解源代码就无法实现的实现细节,它可能会返回其他内容-即使不能保证它不会改变-所以在支持多个.NET版本时必须特别注意。
jweyrich 2015年

8

您还可以编写如下扩展方法:

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 int[10]; //Or any other IList
        var items = new[] {1, 4, 5, 6, 7};
        collection.AddRange(items);

仍然在项目上进行迭代,但是您不必每次编写迭代或进行强制转换。


1

使用LINQ的另一个答案,条件是您要添加的内容是List<T>或可以调用ToList()它:

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

toAdd.ToList().ForEach(target.Add);

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.