这更像是一个关于性能的学术问题,而不是一个现实的“我应该使用什么”,但我很好奇,因为我根本不涉及IL,看看是什么构造的,我没有大数据集在手边反对。
哪个更快:
List<myObject> objs = SomeHowGetList();
List<string> strings = new List<string>();
foreach (MyObject o in objs)
{
if (o.Field == "something")
strings.Add(o.Field);
}
或:
List<myObject> objs = SomeHowGetList();
List<string> strings = new List<string>();
string s;
foreach (MyObject o in objs)
{
s = o.Field;
if (s == "something")
strings.Add(s);
}
请记住,我真的不想知道string.Add(s)的性能影响(因为无论需要做什么操作都无法真正改变),只是每个设置的性能差异迭代(假设s可以是任何原始类型或字符串),每次迭代都会在对象上调用getter。
答案 0 :(得分:9)
你的第一个选择在我的测试中明显加快。我是这样的翻转翻牌!但是说真的,我对原始测试中的代码做了一些评论。这是更新的代码,显示选项2更快。
class Foo
{
public string Bar { get; set; }
public static List<Foo> FooMeUp()
{
var foos = new List<Foo>();
for (int i = 0; i < 10000000; i++)
{
foos.Add(new Foo() { Bar = (i % 2 == 0) ? "something" : i.ToString() });
}
return foos;
}
}
static void Main(string[] args)
{
var foos = Foo.FooMeUp();
var strings = new List<string>();
Stopwatch sw = Stopwatch.StartNew();
foreach (Foo o in foos)
{
if (o.Bar == "something")
{
strings.Add(o.Bar);
}
}
sw.Stop();
Console.WriteLine("It took {0}", sw.ElapsedMilliseconds);
strings.Clear();
sw = Stopwatch.StartNew();
foreach (Foo o in foos)
{
var s = o.Bar;
if (s == "something")
{
strings.Add(s);
}
}
sw.Stop();
Console.WriteLine("It took {0}", sw.ElapsedMilliseconds);
Console.ReadLine();
}
答案 1 :(得分:7)
大多数情况下,您的第二个代码段至少与第一个代码段一样快。
这两个代码段在功能上并不相同。不保证属性在各个访问中返回相同的结果。因此,JIT优化器无法缓存结果(除了微不足道的情况),如果缓存长时间运行的属性的结果,它会更快。请看这个例子:why foreach is faster than for loop while reading richtextbox lines。
但是,对于某些特定情况,例如:
for (int i = 0; i < myArray.Length; ++i)
其中myArray
是一个数组对象,编译器能够检测模式并优化代码并省略绑定的检查。如果缓存Length
属性的结果可能会更慢,如:
int len = myArray.Length;
for (int i = 0; i < myArray.Length; ++i)
答案 2 :(得分:4)
这实际上取决于实施。在大多数情况下,假设(通常的做法/礼貌)财产是廉价的。但是,每个“get”都可以通过某些远程资源进行非缓存搜索。对于标准的简单属性,您永远不会注意到两者之间的真正差异。对于最坏的情况,fetch-once,存储和重用会更快。
我很想使用get
两次,直到我知道存在问题......“过早优化”等等......但是;如果我在紧密循环中使用它,那么我可能会将它存储在一个变量中。除了数组上的Length
,它有特殊的JIT处理;-p
答案 3 :(得分:2)
将值存储在字段中是更快的选择。
虽然方法调用不会产生巨大的开销,但它远远超过将值一次存储到堆栈上的局部变量然后检索它。
我一个人一直这样做。
答案 4 :(得分:2)
通常第二个更快,因为第一个重新计算每次迭代的属性。 这是一个可能需要花费大量时间的例子:
var d = new DriveInfo("C:");
d.VolumeLabel; // will fetch drive label on each call