为什么IList不支持AddRange

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

List.AddRange()
存在,但
IList.AddRange()
不存在。
这让我觉得很奇怪。这背后的原因是什么?

c# .net ilist
4个回答
74
投票

因为界面应该易于实现并且不包含“除了厨房之外的所有内容”。如果添加

AddRange
,则应添加
InsertRange
RemoveRange
(为了对称)。更好的问题是为什么
IList<T>
接口没有类似于
IEnumerable<T>
接口的扩展方法。 (就地
Sort
BinarySearch
、... 的扩展方法很有用)


18
投票

对于那些想要在 IList 上使用“AddRange”、“Sort”等扩展方法的人,

下面是

AddRange
扩展方法:

 public static void AddRange<T>(this IList<T> source, IEnumerable<T> newList)
 {
     if (source == null)
     {
        throw new ArgumentNullException(nameof(source));
     }

     if (newList == null)
     {
        throw new ArgumentNullException(nameof(newList));
     }

     if (source is List<T> concreteList)
     {
        concreteList.AddRange(newList);
        return;
     }

     foreach (var element in newList)
     {
        source.Add(element);
     }
}

我创建了一个小型库来执行此操作。我发现它比必须在每个项目上重做其扩展方法更实用。

有些方法比 List 慢,但它们可以完成工作。

这是他们感兴趣的 GitHub:

IListExtension 存储库


2
投票

实际上,除了

.Net platfom
开发商和建筑师之外,没有人能回答这个问题。但有几点,可能是原因。

在这个答案中,我将谈论非泛型类,但几乎我所有的话也适用于泛型类。

在进行解释之前,我想向那些不知道的人提一下,

List<>
和所有
IList
实现应该是通用编程方面的List和数据结构,这通常意味着链表。在Microsoft IList文档中我们可以看到定义:

表示可以通过索引单独访问的对象集合。

所以,一般来说,阅读这个定义,你一定不会有“为什么

AddRange
不出现在
IList
中”的问题,而是“为什么
Add
出现?”。而且,说到
Add
,它不是在
IList
界面,而是在
ICollection
界面。这是一件非常奇怪的事情。为什么?因为.Net Standard中几乎所有集合都继承了
ICollection
。因此,在 .Net 源代码中有很多地方,我们可以看到
Add
的实现,就像在 Array 类中一样(是的,
Array
也实现了
IList
):

int IList.Add(Object value)
{
   throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
}

关于 C# 中集合接口之间的关系,我可以说更多的事情(还有关于 IReadOnlyList,它是在

IList
之后添加的,看起来像
IList
应该是的东西)。但我认为有足够的背景,我们可以开始谈论为什么
IList
没有
AddRange
,但
List<>
有的具体原因。

  • 并非所有
    IList
    实现都应该具有
    AddRange
    方法。

    正如我上面提到的,

    Add
    方法有问题。 C# 中的很多集合实际上都有它,但在调用它时会抛出
    NotSupportedException
    。使用
    AddRange
    方法也会出现同样的情况(甚至更糟)。所以只有
    List<>
    需要这个方法,但
    IList
    的所有其他实现都不需要它。此外,那些决定创建自己的
    IList
    实现的开发人员将必须实现
    AddRange
    ,这看起来不像是简单索引集合(
    IList
    是)真正需要的东西。

  • AddRange
    强烈依赖于
    List<T>
    的实施。

    谈论

    List<>
    课程。不是一个名为
    List
    的非泛型类。非通用变体称为
    ArrayList
    。在数据结构方面,
    ArrayList
    动态数组的某种同义词。我不知道为什么决定在泛型集合中将
    ArrayList
    重命名为
    List
    ,但我认为这只会增加对 C# 中这些类的误解。所以,
    List<T>
    实际上是一个动态数组。如果将大量元素一一添加到动态数组中,则会产生很大的性能问题。所以
    AddRange
    是动态数组的一种辅助方法,从某种意义上来说,也是必要的方法。但对于索引集合来说,这完全没有必要,而
    IList
    就是这样。

作为结论,我想说,

List<T>
IList<T>
(就像
ArrayList
IList
),事实上,是实体,具有不同的语义,你不能像可互换的东西。但是在命名和接口关系方面做出了一些错误的决定,导致对
List<T>
IList<T>

之间的关系越来越误解

1
投票

从 C#7 开始,我们有了模式匹配,我们可以轻松地使用它来调用性能更高的

List.AddRange()
方法,并且不需要使用
as
保存转换。

public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
{
    if (collection is null)
        throw new ArgumentNullException(nameof(collection));
    if (items is null)
        throw new ArgumentNullException(nameof(items));

    switch (collection)
    {
        case List<T> list:
            list.AddRange(items);
            break;
        default:
            foreach (var item in items)
            {
                collection.Add(item);
            }
            break;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.