Similar Question:
Find() vs. Where().FirstOrDefault()
在具有单个字符串属性的简单引用类型的大序列中搜索 Diana 时得到了一个有趣的结果。
using System;
using System.Collections.Generic;
using System.Linq;
public class Customer{
public string Name {get;set;}
}
Stopwatch watch = new Stopwatch();
const string diana = "Diana";
while (Console.ReadKey().Key != ConsoleKey.Escape)
{
//Armour with 1000k++ customers. Wow, should be a product with a great success! :)
var customers = (from i in Enumerable.Range(0, 1000000)
select new Customer
{
Name = Guid.NewGuid().ToString()
}).ToList();
customers.Insert(999000, new Customer { Name = diana }); // Putting Diana at the end :)
//1. System.Linq.Enumerable.DefaultOrFirst()
watch.Restart();
customers.FirstOrDefault(c => c.Name == diana);
watch.Stop();
Console.WriteLine("Diana was found in {0} ms with System.Linq.Enumerable.FirstOrDefault().", watch.ElapsedMilliseconds);
//2. System.Collections.Generic.List<T>.Find()
watch.Restart();
customers.Find(c => c.Name == diana);
watch.Stop();
Console.WriteLine("Diana was found in {0} ms with System.Collections.Generic.List<T>.Find().", watch.ElapsedMilliseconds);
}
这是因为 List.Find() 中没有 Enumerator 开销,还是因为这加上其他原因?
Find()
运行速度几乎是原来的两倍,希望 .Net 团队将来不会将其标记为过时。
我能够模仿您的结果,所以我反编译了您的程序,Find
和 FirstOrDefault
之间存在差异。
首先是反编译的程序。我让你的数据对象成为一个匿名数据项,只是为了编译
List<\u003C\u003Ef__AnonymousType0<string>> source = Enumerable.ToList(Enumerable.Select(Enumerable.Range(0, 1000000), i =>
{
var local_0 = new
{
Name = Guid.NewGuid().ToString()
};
return local_0;
}));
source.Insert(999000, new
{
Name = diana
});
stopwatch.Restart();
Enumerable.FirstOrDefault(source, c => c.Name == diana);
stopwatch.Stop();
Console.WriteLine("Diana was found in {0} ms with System.Linq.Enumerable.FirstOrDefault().", (object) stopwatch.ElapsedMilliseconds);
stopwatch.Restart();
source.Find(c => c.Name == diana);
stopwatch.Stop();
Console.WriteLine("Diana was found in {0} ms with System.Collections.Generic.List<T>.Find().", (object) stopwatch.ElapsedMilliseconds);
这里要注意的关键是 FirstOrDefault
是在 Enumerable
上调用的,而 Find
是作为源列表上的方法调用的。
那么,find 在做什么?这是反编译后的Find
方法
private T[] _items;
[__DynamicallyInvokable]
public T Find(Predicate<T> match)
{
if (match == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
for (int index = 0; index < this._size; ++index)
{
if (match(this._items[index]))
return this._items[index];
}
return default (T);
}
因此它迭代了一个有意义的项目数组,因为列表是数组的包装器。
但是,FirstOrDefault
在 Enumerable
类上使用 foreach
来迭代项目。这使用列表的迭代器并移动到下一个。我认为您看到的是迭代器的开销
[__DynamicallyInvokable]
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null)
throw Error.ArgumentNull("source");
if (predicate == null)
throw Error.ArgumentNull("predicate");
foreach (TSource source1 in source)
{
if (predicate(source1))
return source1;
}
return default (TSource);
}
Foreach 就是 syntatic sugar关于使用可枚举模式。看这张图
.
我单击 foreach 以查看它在做什么,您可以看到 dotpeek 想要带我到枚举器/当前/下一个实现,这很有意义。
除此之外它们基本相同(测试传入的谓词以查看某个项目是否是您想要的)