为什么将 List<T>.AddRange 方法设为通用方法会影响性能?

Why would making List<T>.AddRange method a generic one be bad for performance?

我正在阅读 C# 深度(第 3 版),在第 13 章中,在讨论在 C# 4 中包含协变和逆变类型参数的部分中,做出了以下声明:

The parameter for List.AddRange is of type IEnumerable<T>, so in this case you’re treating each list as an IEnumerable <IShape>—something that wouldn’t have been possible before. AddRange could have been written as a generic method with its own type parameter, but it wasn’t—doing this would’ve made some optimizations hard or impossible.

有人可以为这种说法提供一些理由吗?不清楚为什么对我来说是这样。

我认为原因是,IEnumerable<T> 可能只包含 type/implement 接口 T 的对象,并且需要检查 T 中的每个元素的这种情况=13=]

让我们尝试一些会因通用 AddRange

而崩溃的代码
public class MyClass1 : IFoo, IBar
{
  /* some code */
}

public class MyClass2 : IFoo
{
  /* some code */
}

var fooList = new List<IFoo>
{
  new MyClass1(),
  new MyClass2()
}
var barList = new List<IBar>();
barList.AddRange<IFoo>(fooList);

现在,问题是,它应该如何反应?您可以将 MyClass1 的对象添加到 barList,因为它实现了 IBar,但是当您尝试添加 MyClass2 时会发生什么?我们需要在添加列表之前检查列表中的每个元素,以防止抛出异常。

我猜它没有写成 void AddRange<T>(IEnumerable<T> items) 是因为它在 IEnumerable<T>ICollection<T> 时进行了优化。当IEnumerable<T>ICollection<T>时,AddRange内部调用ICollection<T>.CopyTo,第一个参数为T[]。 (请注意,List<T> 的底层存储机制是 T[])。

基本类型的数组与派生类型的数组不同,因此您不能这样做,例如:

object[] objs = new object[4];
var collection = (new string[4]) as ICollection<string>;
collection.CopyTo(objs,0); //Cannot convert object[] to string[]

这是 "impossible" 的优化。

您可以在此处查看源代码: https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,79de3e39e69a4811

似乎 AddRange 应该检查 T[]List<T>,并在这些情况下执行 Array.Copy,但我猜是 -100。您可能会对 Array.ToArray() 没有做的事情感到有些惊讶。