List.AddRange()
存在,但 IList.AddRange()
不存在。因为界面应该易于实现并且不包含“除了厨房之外的所有内容”。如果添加
AddRange
,则应添加 InsertRange
和 RemoveRange
(为了对称)。更好的问题是为什么 IList<T>
接口没有类似于 IEnumerable<T>
接口的扩展方法。 (就地 Sort
、BinarySearch
、... 的扩展方法很有用)
对于那些想要在 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:
实际上,除了
.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>
之间的关系越来越误解
从 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;
}
}