在 foreach 循环内部或外部声明一个变量: 哪个更快/更好?

哪一个更快/更好?

这个:

List<User> list = new List<User>();
User u;


foreach (string s in l)
{
u = new User();
u.Name = s;
list.Add(u);
}

或者这个:

List<User> list = new List<User>();


foreach (string s in l)
{
User u = new User();
u.Name = s;
list.Add(u);
}

我的新手发展技能告诉我第一个更好,但我的一个朋友告诉我我错了,但不能给我一个好的理由为什么第二个更好。

在性能上有什么区别吗?

52353 次浏览

The 2nd one is better. You are meaning to have a new user in each iteration.

There should be no percievable difference in performance.

Performance-wise both examples are compiled to the same IL, so there's no difference.

The second is better, because it more clearly expresses your intent if u is only used inside the loop.

Technically, the first example will save a few nanoseconds because the stack frame will not have to be moved to allocate a new variable, but this is such a tiny amount of CPU time you won't notice it, that's if the compiler doesn't optimize any difference away anyays.

A declaration does not cause any code to be executed, so it's not a performance issue.

The second one is what you mean, and you're less likely to make a stupid error if you do it the second way, so use that. Always try to declare variables in the smallest scope necessary.

And besides, the better way is to use Linq:

List<User> users = l.Select(name => new User{ Name = name }).ToList();

In this scenario, the second version is better.

In general, if you only need to access the value within the body of the iteration, then choose the second version. On the other hand, if there is some final state the variable will hold beyond the body of the loop, then declare then use the first version.

In any case, the best way would be to use a constructor that takes a Name... or, otherwise, exploit curly-brace notation:

foreach (string s in l)
{
list.Add(new User(s));
}

or

foreach (string s in l)
{
list.Add(new User() { Name = s });
}

or even better, LINQ:

var list = l.Select( s => new User { Name = s});

Now, while your first example could, in some cases, be unperceptibly faster, the second one is better because it's more readable, and the compiler may discard the variable (and omit it altogether) since it's not used outsid the foreach's scope.

Whenever you've a question about performance, the only thing to do is measure - run a loop around your test and time it.

To answer your question - without measuring :-) or looking at the generated ilasm - any difference wouldn't be noticeable in a meaningful number of iterations and the most expensive operation in your code there is likely to be the user allocation by a few orders of magnitude, so concentrate on code clarity (as you should in general) and go with 2.

Oh, its late and I guess I'm just trying to say don't worry about this sort of thing or get caught up in details like this.

K

I went to verify this issue.

namespace Test
{
class Foreach
{
string[] names = new[] { "ABC", "MNL", "XYZ" };


void Method1()
{
List<User> list = new List<User>();
User u;


foreach (string s in names)
{
u = new User();
u.Name = s;
list.Add(u);
}
}


void Method2()
{


List<User> list = new List<User>();


foreach (string s in names)
{
User u = new User();
u.Name = s;
list.Add(u);
}
}
}


public class User { public string Name; }
}

CIL spills out that only variables get a different numbering.

enter image description here

So I prepared something what was supposed to be much better one.

namespace Test
{
class Loop
{


public TimeSpan method1 = new TimeSpan();
public TimeSpan method2 = new TimeSpan();


Stopwatch sw = new Stopwatch();


public void Method1()
{
sw.Restart();


C c;
C c1;
C c2;
C c3;
C c4;


int i = 1000;
while (i-- > 0)
{
c = new C();
c1 = new C();
c2 = new C();
c3 = new C();
c4 = new C();
}


sw.Stop();
method1 = method1.Add(sw.Elapsed);
}


public void Method2()
{
sw.Restart();


int i = 1000;
while (i-- > 0)
{
var c = new C();
var c1 = new C();
var c2 = new C();
var c3 = new C();
var c4 = new C();
}


sw.Stop();
method2 = method2.Add(sw.Elapsed);
}
}


class C { }
}

CIL shows no difference.

enter image description here

As was already pointed out declaration is not allocation so there is no performance penalty on it.

Test

namespace Test
{
class Foreach
{
string[] names = new[] { "ABC", "MNL", "XYZ" };


public TimeSpan method1 = new TimeSpan();
public TimeSpan method2 = new TimeSpan();


Stopwatch sw = new Stopwatch();


void Method1()
{
sw.Restart();


List<User> list = new List<User>();
User u;


foreach (string s in names)
{
u = new User();
u.Name = s;
list.Add(u);
}


sw.Stop();
method1 = method1.Add(sw.Elapsed);
}


void Method2()
{
sw.Restart();


List<User> list = new List<User>();


foreach (string s in names)
{
User u = new User();
u.Name = s;
list.Add(u);
}


sw.Stop();
method2 = method2.Add(sw.Elapsed);
}
}


public class User { public string Name; }