3

在一段代码中

List<int> foo = new List<int>() { 1, 2, 3, 4, 5, 6 };
IEnumerable<int> bar = foo.Where(x => x % 2 == 1);

barSystem.Linq.Enumerable.WhereListIterator<int>由于延迟执行而属于类型。由于它实现IEnumerable<int>了可以将其转换为List<int>using ToList()。但是,我无法识别ToList()调用时运行的代码的某些部分。我正在使用 dotPeek 作为反编译器,这是我第一次尝试这样的事情,所以如果我在途中犯了任何错误,请纠正我。

我将在下面描述我到目前为止发现的内容(所有程序集都是版本 4.0.0.0):

  1. Enumerable.WhereArrayIterator<TSource>在程序集中Enumerable.cs命名空间的文件中实现。该类既不定义自身也不实现。它实现了位于同一文件中的内容。确实执行。System.LinqSystem.CoreToList()IEnumerable<TSource>Enumerable.Iterator<TSource>Enumerable.Iterator<TSource>IEnumerable<TSource>

  2. ToList()是一个扩展方法,也位于Enumerable.cs. 它所做的只是空检查,然后List<TSource>用它的参数调用构造函数。

  3. List<T>在程序集中List.cs命名空间的文件中定义。被调用的构造函数具有签名。它再次进行空检查,然后将参数转换为. 如果集合没有元素,则创建一个空数组的新列表,否则使用该方法创建新列表。System.Collections.GenericmscorlibToList()public List(IEnumerable<T> collection)ICollection<T>ICollection.CopyTo()

  4. ICollection<T>mscorlib\中System.Collections.Generic定义ICollection.cs。它IEnumerable以通用和非通用形式实现。

这就是我卡住的地方。既不Enumerable.WhereArrayIterator<TSource>也不Enumerable.Iterator<TSource>实现ICollection,所以在某个地方,必须发生强制转换,我无法找到CopyTo()调用时运行的代码。

4

2 回答 2

3

这是List<T>构造函数(ILSpy)中的相关部分:

ICollection<T> collection2 = collection as ICollection<T>; // this won't succeed
if (collection2 != null) 
{
    int count = collection2.Count;
    this._items = new T[count];
    collection2.CopyTo(this._items, 0);
    this._size = count;
    return;
}
// this will be used instead
this._size = 0;
this._items = new T[4];
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        this.Add(enumerator.Current);
    }
}

因此,您会看到collection as ICollection<T>;尝试转换为ICollection<T>,如果有效,CopyTo则将使用有效的,否则将完全枚举该序列。

WhereListIterator<int>是一个查询而不是一个集合,所以它不能被强制转换为ICollection<T>,因此它将被枚举。

于 2013-07-18T15:03:30.307 回答
1

我认为您对as运营商感到困惑。这基本上是一个安全的演员阵容。它相当于这个,但要快一点:

MyEndType x = null;
if (MyVarWithAs is MyEndType) x = (MyEndType)MyVarWithAs;

现在,让我们再次看一下代码。

 public List(IEnumerable<T> collection)
    {
      if (collection == null)
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
      ICollection<T> collection1 = collection as ICollection<T>;
      if (collection1 != null)
      {
        int count = collection1.Count;
        if (count == 0)
        {
          this._items = List<T>._emptyArray;
        }
        else
        {
          this._items = new T[count];
          collection1.CopyTo(this._items, 0);
          this._size = count;
        }
      }
      else
      {
        this._size = 0;
        this._items = List<T>._emptyArray;
        foreach (T obj in collection)
          this.Add(obj);
      }
    }

如您所见,在if它检查它是否是null. 如果是null,则表示它不是ICollection<T>所以它转到else。所有else所做的都是将所有内容设置为默认值,然后手动添加所有内容。当您传入一个不是的IEnumerable<T>ICollection<T>您的示例中)时,它将通过else路径。

于 2013-07-18T15:09:13.897 回答