.net 为什么IList不支持AddRange

k97glaaz  于 2022-11-26  发布在  .NET
关注(0)|答案(4)|浏览(177)

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

j1dl9f46

j1dl9f461#

因为一个接口应该易于实现,并且不包含“除了厨房之外的所有东西”。更好的问题是为什么IList<T>接口没有类似于IEnumerable<T>接口的扩展方法。(用于就地SortBinarySearch ...的扩展方法将很有用)

kqhtkvqz

kqhtkvqz2#

对于那些希望在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 repository

9q78igpj

9q78igpj3#

实际上,除了.Net platfom开发人员和架构师,没有人能回答这个问题。
在这个答案中,我将谈论非泛型类,但几乎我所有的话对泛型类也是正确的。
在我开始解释之前,我想对那些不知道的人提一下,List<>和所有IList实现并不是根据通用编程和数据结构应该是List,通常是指 * 链表 *。
表示可通过索引单独访问的对象集合。
所以,一般来说,阅读这个定义,你一定不会有“为什么AddRange没有呈现在IList中“的问题,而是“为什么Add呈现了"的问题。而且,说到Add,它不在IList接口中,而是在ICollection接口中。这真伊萨奇怪。为什么呢?因为.Net标准中几乎所有的集合都继承了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。同样的情况(甚至更糟)。因此,只有List<>需要此方法,但IList的所有其他实现都不需要此方法。此外,那些开发人员,决定创建自己的IList实现的用户必须实现AddRange,这看起来不像是简单索引集合(即IList)真正需要的东西。

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

说到List<>类。它不是非泛型类List。非泛型变量称为ArrayList。而ArrayList在数据结构上是Dynamic Array的某种同义词。我不知道为什么决定在泛型集合中将ArrayList重命名为List。但我认为这只会增加对C#中这些类的误解。所以,List<T>实际上是一个动态数组。如果你一个一个地向动态数组添加大量的元素,动态数组会有很大的性能问题。所以,AddRange是一个辅助,从某种意义上说,动态数组的必要方法。但是索引集合完全不需要,索引集合就是IList
作为结论,我想说,List<T>IList<T>(就像ArrayListIList),事实上,是实体,它们有不同的语义,你不能把它们看作是可互换的东西。但是在命名和接口关系方面有一些不好的决定,导致人们对List<T>IList<T>之间关系的认识越来越不正确

6ss1mwsb

6ss1mwsb4#

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

相关问题