在性能方面,对于foreach循环,我们应该在循环之前声明变量还是在循环内部声明?

6

在性能方面,是将变量声明在foreach语句之外并每次在其中重新分配它更好,还是在foreach内部创建一个新变量呢?例如:

private List<ListItem> GetItems()
        {
            var items = new List<ListItem>();
            var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            ListItem item;
            foreach (var i in collection)
            {
                item = new ListItem { Text = i.ToString() };
                items.Add(item);
            }

            return items;
        }

这一个还是那一个?
private List<ListItem> GetItems()
        {
            var items = new List<ListItem>();
            var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            foreach (var i in collection)
            {
                ListItem item = new ListItem { Text = i.ToString() };
                items.Add(item);
            }

            return items;
        }

我在这里谈论的是物品对象(item object)。 谢谢大家。

8个回答

12

这听起来像是一种过早优化

首先,你有没有理由相信这里存在性能问题?

其次,在发布版本中,编译器的优化器可能会为两种情况生成完全相同的代码,因此它可能是无关紧要的。在调试版本中,这并不总是正确的,但你不需要优化,因为调试版本的目的是允许你准确地逐步执行代码。


7

有一个特殊情况需要注意;如果你将变量“捕获”到匿名方法/lambda中。否则,这是过早的且没有任何区别。

一个例子说明其重要性:

// prints all items in no particular order
foreach (var i in collection)
{
    string s = i.ToString();
    ThreadPool.QueueUserWorkItem(delegate { Console.WriteLine(s); });
}

vs

// may print the same item each time, or any combination of items; very bad
string s;
foreach (var i in collection)
{
    s = i.ToString();
    ThreadPool.QueueUserWorkItem(delegate { Console.WriteLine(s); });
}

4

我非常确定您两个代码块所生成的IL代码是相同的,性能不应有任何变化。不过第二个代码块,在您声明item类型并将其用于代码块中,稍微更易读一些,因此我建议使用这种方式。


3

这是一个非常微小的优化,两种方法在性能方面可能完全相同,甚至生成相同的代码。在这种情况下,请选择可读性更好的方法。我更喜欢第二种方法,因为您的对象仅在foreach循环内使用。

可以说,您也可以完全摆脱存储的引用:

private List<ListItem> GetItems()
{
  var items = new List<ListItem>();
  var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

  foreach (var i in collection)
  {
    items.Add(new ListItem { Text = i.ToString() });
  }

  return items;
}

这通常是我所做的。这是自私的,所以我会给你点赞 :D - Ty.

1

这两个代码块生成的IL应该几乎相同。如果您想要进行优化,建议在填充列表项之前设置最终列表的长度。这样,您就不会因为扩展列表长度而受到扩容惩罚。

可以尝试以下代码:

  private List<ListItem> GetItems()
    {
        var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        var items = new List<ListItem>(collection.Count);  //declare the amount of space here

        foreach (var i in collection)
        {
            ListItem item = new ListItem { Text = i.ToString() };
            items.Add(item);
        }

        return items;
    }

0

对于你的情况来说,更好的是:

private List<ListItem> GetItems()        
{            
   var items = new List<ListItem>();            
   var collection = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };            
   foreach (var i in collection)            
      items.Add(new ListItem { Text = i.ToString() });                 
   return items;        
}

为什么要创建额外的变量呢?


0

可能编译成相同的代码,但为什么要重新声明呢。 这就是引用的好处,在这种情况下是 item。 一旦你完成了它,你可以将它分配给另一个 ListItem,GC 会处理剩下的事情。

但另一方面,对其他程序员来说可读性很重要。这是一个决定,肯定不会极大地改变您的应用程序性能。


0

正如大家所猜测的那样,IL将是相同的。另外,正如其他人所提到的,不要担心这些问题,直到它们成为问题。相反,问问自己这个变量的范围属于哪里。

该代码块的范围和上下文比微小的性能优化更重要,这种优化可能过早而且在这种情况下是不必要的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接