Глядя на System.Linq.Enumerable через отражатель я заметил, что по умолчанию итератор используется для Выбрать и Где методы расширения - WhereSelectArrayIterator - не реализует ICollection интерфейса. Если я прочитал код правильно это вызывает некоторые другие методы расширения, такие как Count() и ToList() работает медленнее:Почему не WhereSelectArrayIterator реализует ICollection?
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
// code above snipped
if (source is List<TSource>)
{
return new WhereSelectListIterator<TSource, TResult>((List<TSource>) source, null, selector);
}
// code below snipped
}
private class WhereSelectListIterator<TSource, TResult> : Enumerable.Iterator<TResult>
{
// Fields
private List<TSource> source; // class has access to List source so can implement ICollection
// code below snipped
}
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
{
public List(IEnumerable<T> collection)
{
ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
int count = is2.Count;
this._items = new T[count];
is2.CopyTo(this._items, 0); // FAST
this._size = count;
}
else
{
this._size = 0;
this._items = new T[4];
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
this.Add(enumerator.Current); // SLOW, CAUSES ARRAY EXPANSION
}
}
}
}
}
Я проверил это с результатами, подтверждающие мое подозрение :
ICollection: 2388.5222 мс
IEnumerabl е: 3308.3382 мс
Вот код теста:
// prepare source
var n = 10000;
var source = new List<int>(n);
for (int i = 0; i < n; i++) source.Add(i);
// Test List creation using ICollection
var startTime = DateTime.Now;
for (int i = 0; i < n; i++)
{
foreach(int l in source.Select(k => k)); // itterate to make comparison fair
new List<int>(source);
}
var finishTime = DateTime.Now;
Response.Write("ICollection: " + (finishTime - startTime).TotalMilliseconds + " ms <br />");
// Test List creation using IEnumerable
startTime = DateTime.Now;
for (int i = 0; i < n; i++) new List<int>(source.Select(k => k));
finishTime = DateTime.Now;
Response.Write("IEnumerable: " + (finishTime - startTime).TotalMilliseconds + " ms");
Я-то отсутствует или это будет исправлено в будущих версиях рамки?
Благодарим вас за мысли.
Спасибо - это то, что надоедливый предикат, который мешает ему заранее знать его размер. Я бы сделал случай, когда списки процессов, не изменяя их размер, являются частыми действиями, и, возможно, whereSelectIterrator может быть подклассом SelectIterrator, который _could_ реализует icollection. В любом случае, еще раз спасибо. –
на самом деле, поцарапать это, отменить его: WhereSelectIterrator должен быть родительским классом, а SelectIterrator должен быть подклассом, реализующим ICollection –