C# 按类型划分的Linq查询性能
工作中出现了一个讨论: 我们有一个类有一个IList。事实是一个抽象基类,有几个具体的子类(PopulationFact、GdpFact等) 最初,我们会以这种方式查询给定的事实,即按类型:C# 按类型划分的Linq查询性能,c#,.net,performance,linq,C#,.net,Performance,Linq,工作中出现了一个讨论: 我们有一个类有一个IList。事实是一个抽象基类,有几个具体的子类(PopulationFact、GdpFact等) 最初,我们会以这种方式查询给定的事实,即按类型: .Facts.FirstOrDefault(x => x.Year == 2011 && x is GdpFact) 然而,现在有人提出了一个问题,我们是否应该引入FactType枚举来代替它 .Facts.FirstOrDefault(x => x.Year == 2011
.Facts.FirstOrDefault(x => x.Year == 2011 && x is GdpFact)
然而,现在有人提出了一个问题,我们是否应该引入FactType枚举来代替它
.Facts.FirstOrDefault(x => x.Year == 2011 && x.FactType == FactType.Gdp)
这项建议之所以被提出,是因为它据说速度更快。我承认,我没有编写任何测试来尝试辨别性能上的差异,但我有两个问题:
1) 像这样的“类型查询”本质上是坏的吗?2) 考虑到事实是强类型的,添加FactType枚举不是多余的吗 更新 澄清一下,这是LinqtoObjects和GdpFact:Fact 更新2 我们使用当前的典型数据(4个事实)进行了测量,结果如下: 在枚举上查找:0.29660000000000003毫秒 查找类型:0.245300000000000002毫秒
因此,在这种情况下,类型查找速度更快!我会仔细选择我接受的答案 所有类型的性能相关问题都严格取决于具体的应用环境,因此此处提供的答案可能部分正确/错误,适用于您的具体案例 考虑到这一点: 检查枚举值应该比检查类型快得多,因为在第一种情况下,您只需检查等式
2
整数(枚举值)
但这在对象中又引入了一个字段,必须对其进行跟踪以获得正确的值(单元测试),在第二种情况下不需要它,因为CLR
关心正确的类型初始化
我认为最好根据相关的数据量来分析您的想法,您的应用程序通常会重新运行,并为您提供正确的想法。这可能是一个寻找问题的解决方案吗 我认为在基类型中同时使用具体的子类型和枚举可能会混淆您的设计。您可以想象有人后来编写了一个新的具体类,但没有意识到他们也需要添加到枚举中
除非您发现您在性能方面有特定的问题,否则我会倾向于优先考虑清晰性。因此,如果您需要不同的具体类(我假设您需要,因为这是您开始编写的代码),那么我会坚持使用您的类型,而不是使用枚举。我认为您最初的方法是好的。为此目的提供了“is”关键字。不鼓励使用“is”。使用枚举似乎过于工程化了。我们应该尽量使代码简单。在大多数情况下,代码行越少越好。检查枚举值可能比运行时类型检查快,但是
例如,什么能阻止你或你的同事意外地做这样的事情
public class PopulationFact : Fact
{
public FactType FactType = FactType.GdpFact; // should be PopulationFact
}
.Facts.FirstOrDefault(x => x.Year == 2011).OfType<GdpFact>()
.Facts.FirstOrDefault(x=>x.Year==2011).OfType()的
我做了一个测试,1000000次迭代的结果大约是
ByCast 166ms
ByType 84ms
ByEnum 98ms
因此,enum
实际上是多余的,速度较慢,但不会太快。这应该不会太令人惊讶,类型系统是.Net框架的基础
下面转录的测试代码,对勘误表表示歉意
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
class Program
{
private enum TypeOfFact
{
Gdp,
Other
}
private abstract class Fact
{
public virtual int Year { get; set; }
public abstract TypeOfFact FactType { get; }
}
private class GdpFact : Fact
{
public override TypeOfFact FactType
{
get { return TypeOfFact.Gdp; }
}
}
private class OtherFact : Fact
{
public override TypeOfFact FactType
{
get { return TypeOfFact.Other; }
}
}
static void Main()
{
Ilist<Fact> facts = new List<Fact>
{
new GdpFact { Year = 2010 },
new OtherFact { Year = 2010 },
new GdpFact { Year = 2009 },
new OtherFact { Year = 2009 },
new GdpFact { Year = 2011 },
new OtherFact { Year = 2011 },
};
const int interations = 1000000;
var funcs = new List<Func<IList<Fact>, Fact>>
{
ByList,
ByType,
ByEnum
};
// Warmup
foreach (var func in funcs)
{
Measure(5, func, facts);
}
// Results
foreach (var result in funcs.Select(f => new
{
Description = f.Method.Name,
Ms = Measure(iterations, f, facts)
}))
{
Console.WriteLine(
"{0} time = {1}ms",
result.Description,
result.Ms);
}
}
private static long Measure(
int iterations,
Func<IList<Fact>, Fact> func,
IList<Fact> facts)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
for (var i = 0; i < iterations; i++)
{
func.Invoke(facts);
}
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
private static Fact ByType(IList<Fact> facts)
{
return facts.FirstOrDefault(f =>
f.Year == 2011 && f is GdpFact);
}
private static Fact ByEnum(IList<Fact> facts)
{
return facts.FirstOrDefault(f =>
f.Year == 2011 && f.FactType == TypeOfFact.Gdp);
}
private static Fact ByCast(IList<Fact> facts)
{
return facts.OfType<GdpFact>()
.FirstOrDefault(f => f.Year == 2011);
}
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.Linq;
班级计划
{
私有枚举TypeOfFact
{
国内生产总值,
其他
}
私有抽象类事实
{
公共虚拟整数年{get;set;}
公共抽象类型事实类型{get;}
}
私有类GdpFact:事实
{
公共覆盖类型OFFACT FactType
{
获取{return TypeOfFact.Gdp;}
}
}
私有类其他事实:事实
{
公共覆盖类型OFFACT FactType
{
获取{return TypeOfFact.Other;}
}
}
静态void Main()
{
Ilist facts=新列表
{
新的GdpFact{Year=2010},
新的其他事实{Year=2010},
新的GdpFact{Year=2009},
新的其他事实{Year=2009},
新的GdpFact{Year=2011},
新的其他事实{Year=2011},
};
const int interactions=1000000;
var funcs=新列表
{
署名者,
按类型,
拜恩姆
};
//热身
foreach(funcs中的var func)
{
措施(5,职能,事实);
}
//结果
foreach(var)在funcs.Select中生成结果(f=>new
{
Description=f.Method.Name,
Ms=度量(迭代、f、事实)
}))
{
控制台写入线(
“{0}时间={1}毫秒”,
结果.说明,
结果(Ms);
}
}
私有静态长度量(
整数迭代,
Func Func,
(事实)
{
var stopwatch=新秒表();
秒表。开始();
对于(var i=0;i
f、 年份==2011年&f为GdpFact);
}
私有静态事实ByEnum(IList事实)
{
返回事实。FirstOrDefault(f=>
f、 年份==2011年&f.FactType==TypeOfFact.Gdp);
}
私有静态事实广播(IList事实)
{
R