假设我有一个不同类型对象的集合objs
,我想检查是否至少有一个项具有指定类型MyType
。什么解决方案更好?
解决方案1:
bool found = objs.OfType<MyType>().Any();
解决方案2:
bool found = objs.Any(o => o is MyType);
有什么不同吗?
这个问题不仅适用于Any((,也适用于其他LINQ方法。
为了好玩,我使用BenchmarkDotNet:构建了一个基准
public class MyType { }
public interface IMyType { }
public struct MyStruct { }
namespace Benchmark
{
public class MyBenchmark
{
private readonly List<object> classes = new List<object>();
private readonly List<MyStruct> structs = new List<MyStruct>();
public MyBenchmark()
{
for (int i = 0; i < 100; i++)
{
classes.Add(new object());
structs.Add(new MyStruct());
}
}
[Benchmark]
public bool OfTypeClass()
{
return classes.OfType<MyType>().Any();
}
[Benchmark]
public bool AnyClass()
{
return classes.Any(x => x is MyType);
}
[Benchmark]
public bool OfTypeStruct()
{
return structs.OfType<IMyType>().Any();
}
[Benchmark]
public bool AnyStruct()
{
return structs.Any(x => x is IMyType);
}
}
class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<MyBenchmark>();
}
}
}
结果:
BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18363
Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.200-preview-014883
[Host] : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
| Method | Mean | Error | StdDev |
|------------- |-----------:|---------:|---------:|
| OfTypeClass | 1,697.2 ns | 29.92 ns | 27.98 ns |
| AnyClass | 1,477.2 ns | 18.77 ns | 14.65 ns |
| OfTypeStruct | 1,895.0 ns | 37.45 ns | 60.48 ns |
| AnyStruct | 820.6 ns | 13.57 ns | 11.33 ns |
我们可以看到,Any
比OfType
稍微快一点(正如预期的那样,OfType
对结构来说稍微贵一点,因为它需要对它们进行装箱(,但没有太大意义
令人好奇的是,AnyStruct
要快得多。我认为JIT能够发现MyStruct
没有实现IMyType
,并显著地优化了事情。
如果我们看看OfType((源代码(https://github.com/microsoft/referencesource/blob/master/System.Core/System/Linq/Enumerable.cs),我们可以看到
public static IEnumerable<TResult> OfType<TResult>(this IEnumerable source) {
if (source == null) throw Error.ArgumentNull("source");
return OfTypeIterator<TResult>(source);
}
static IEnumerable<TResult> OfTypeIterator<TResult>(IEnumerable source) {
foreach (object obj in source) {
if (obj is TResult) yield return (TResult)obj;
}
}
所以基本上,它们是一样的。