在.NET中,使用“ foreach”迭代IEnumerable 的实例将创建副本吗?因此,我应该更喜欢使用“ for”而不是“ foreach”吗?

| 在.NET中,使用\“ foreach \”迭代IEnumerable的实例将创建副本吗?因此,我应该更喜欢使用\“ for \”而不是\“ foreach \”吗? 我写了一些代码来证明这一点:
struct ValueTypeWithOneField
{
    private Int64 field1;
}

struct ValueTypeWithFiveField
{
    private Int64 field1;
    private Int64 field2;
    private Int64 field3;
    private Int64 field4;
    private Int64 field5;
}

public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(\"one field\");
        Test<ValueTypeWithOneField>();

        Console.WriteLine(\"-----------\");

        Console.WriteLine(\"Five field\");
        Test<ValueTypeWithFiveField>();

        Console.ReadLine();
    }

    static void Test<T>()
    {
        var test = new List<T>();
        for (int i = 0; i < 5000000; i++)
        {
            test.Add(default(T));
        }

        Stopwatch sw = new Stopwatch();

        for (int i = 0; i < 5; i++)
        {
            sw.Start();

            foreach (var item in test)
            {

            }

            sw.Stop();
            Console.WriteLine(\"foreach \" + sw.ElapsedMilliseconds);
            sw.Restart();

            for (int j = 0; j < test.Count; j++)
            {
                T temp = test[j];
            }

            sw.Stop();
            Console.WriteLine(\"for \" + sw.ElapsedMilliseconds);
            sw.Reset();
        }
    }}
这是我运行代码后得到的结果:
    one field
    foreach 68
    for 72
    foreach 68
    for 72
    foreach 67
    for 72
    foreach 64
    for 73
    foreach 68
    for 72
    -----------
    Five field
    foreach 272
    for 193
    foreach 273
    for 191
    foreach 272
    for 190
    foreach 271
    for 190
    foreach 275
    for 188
正如我们在结果中看到的,\“ foreach \”总是比\“ for \”花费更多的时间。 因此,在遍历值类型的通用集合时,我应该更喜欢使用\“ for \”而不是\“ foreach \”吗? 注意:感谢您的提醒,我编辑了代码和结果。但仍然,foreach的运行速度比foreach慢。     
已邀请:
您的测试不准确;在
foreach
版本中,您实际上是在旋转枚举数并从列表中检索每个值(即使您没有使用它)。在
for
版本中,除了查看
Count
属性外,您对列表完全不做任何事情。本质上,您正在测试遍历集合的枚举器与将整数变量递增相等次数相比的性能。 要创建奇偶校验,您需要声明一个临时变量,并在“ 3”循环的每次迭代中分配它。 话虽如此,您的问题的答案是肯定的。每次赋值或
return
语句都会创建该值的副本。 性能 这种伪代码分解应解释为什么在这种特殊情况下,
foreach
比使用
for
要慢一些:
foreach
try
{
    var en = test.GetEnumerator(); //creates a ListEnumerator
    T item;

    while(en.MoveNext()) // MoveNext increments the current index and returns
                         // true if the new index is valid, or false if it\'s
                         // beyond the end of the list. If it returns true,
                         // it retrieves the value at that index and holds it 
                         // in an instance variable
    {
        item = en.Current; // Current retrieves the value of the current instance
                           // variable
    }
}
finally { }
for
int index = -1;
T item;

while(++index < test.Count)
{
    item = test[index];
}
如您所见,在
for
实现中代码很少,而
foreach
for
之上有一个抽象层(枚举器)。我使用
while
循环编写了
for
,以类似的方式显示了两个版本。 说了这么多... 您正在谈论执行时间的微小差异。使用使代码更清晰,更小的循环,在这种情况下,看起来像
foreach
。     
您的问题是方法,方法太复杂。分解。   使用“ foreach”迭代值类型的序列是否会创建序列的副本? 没有。   使用\“ foreach \”迭代值类型序列是否会创建每个值的副本? 是。   使用\“ for \”对值类型的索引序列进行等效迭代是否会创建每个值的副本? 通常是的。如果您知道有关集合的特殊内容,例如可以说它是一个数组,可以采取一些措施来避免复制。但是在一般情况下,对索引集合进行索引时,对序列进行索引将返回序列中值的副本,而不是对包含该值的存储位置的引用。   对值类型执行任何操作会复制值吗? 大概。值类型按值复制。这就是为什么它们被称为值类型的原因。对不进行复制的值类型所做的唯一事情就是调用值类型的方法,并使用\“ out \”或\“ ref \”传递值类型变量。值类型会不断复制;这就是为什么值类型通常比引用类型慢的原因。   使用\“ foreach \”或\“ for \”迭代引用类型序列是否会复制引用? 是。引用类型的表达式的值是引用。该引用在使用时会被复制。   那么就复制行为而言,值类型和引用类型之间有什么区别? 值类型按值复制。引用类型复制引用,但不复制所引用的事物。每次使用16字节值类型时,都会复制16字节。每次使用16字节引用类型时,都会复制4(或8)字节引用。   foreach循环是否比for循环慢? 通常是这样。 foreach循环通常会做更多的工作,因为它正在创建一个枚举数并在该枚举数上调用方法,而不仅仅是增加整数。整数增量非常快。同样不要忘记,必须在foreach循环中放置枚举数,这同样会花费时间。   我是否应该使用for循环而不是foreach循环,因为for循环有时会快几微秒? 不,那太蠢了。您应该基于以客户为中心的经验数据做出明智的工程决策。 foreach循环的额外负担很小。客户可能永远不会注意到。您应该做的是: 根据客户意见设定绩效目标 衡量是否达到目标 如果还没有,请使用探查器找到最慢的东西 修理它 重复直到达到目标 奇妙的是,如果遇到性能问题,将foreach循环更改为for循环不会对您的问题产生任何影响。首先以清晰易懂的方式编写代码。     
您无需在“ for”测试之后重置“秒表”,因此将“ for”测试中花费的时间添加到后续的“ foreach”测试中。同样,如正确指定的那样,您应该在\'for \'内部进行赋值以模仿foreach的确切行为。
sw.Start();

foreach (var item in test)
{

}

sw.Stop();
Console.WriteLine(\"foreach \" + sw.ElapsedMilliseconds);
sw.Restart();

for (int j = 0; j < test.Count; j++)
{
    T temp = test[j];
}

sw.Stop();
Console.WriteLine(\"for \" + sw.ElapsedMilliseconds);
sw.Reset(); // -- This bit is missing!
    
在您的
for
周期中,我看不到您实际从
test
访问项目。如果将
var x = test[i];
添加到
for
周期中,您会发现其性能(实际上)是相同的。 每次对value-type属性的访问都会创建一个副本,可以在
for
周期中使用
foreach
或在
list
上使用
indexer
。     
这是关于主题的讨论,为什么我应该在循环中使用foreach而不是for(int i = 0; i
我认为foreach提供了一种抽象的循环方式,但从技术上讲它比for循环慢,可以在这里找到有关for循环和foreach之间区别的好文章。     
您的考试不公平。考虑“ 2”循环的工作方式。您有以下代码:
foreach (var item in test)
{

}
这将创建一个变量“ 30”,并在每次迭代时从集合中获取下一个对象,并将其分配给“ 30”。这种获取和分配不应创建副本,但是访问基础集合并将正确的值分配给变量确实需要花费时间。 然后,您有以下代码:
for (int j = 0; j < test.Count; j++)
{

}
这根本不访问基础集合。它不会在每次迭代中读取和分配变量。它只是将整数增加33倍,因此当然更快。如果编译器很聪明,它将看到循环中没有任何操作发生,而只是优化整个过程。 公平的比较将把第二部分代码替换为:
var item;
for (int j = 0; j < test.Count; j++)
{
    item = test.get(j);
} 
这更类似于您的
foreach
循环所做的事情。 至于使用哪种,这实际上是个人喜好和编码风格的问题。从可读性的角度来看,我通常认为
foreach
for(...)
更清晰。     
我发现只有一种情况很重要-针对Windows Phone 7开发。有两个原因需要更改
foreach(var item in colletion)
{
}
int length = array.Length;
for(int i = 0; i < length; ++i)
{
}
在XNA游戏中,如果集合很大或经常调用(例如Update方法)。 快一点 减少垃圾 垃圾是至关重要的,因为Compact Framework GC每1MB分配都会触发一次,因此,它可能导致令人讨厌的冻结。     

要回复问题请先登录注册