Performance-khôn ngoan, chúng ta hãy thử ví dụ cụ thể:
public void Method1()
{
foreach(int i in Enumerable.Range(0, 10))
{
int x = i * i;
StringBuilder sb = new StringBuilder();
sb.Append(x);
Console.WriteLine(sb);
}
}
public void Method2()
{
int x;
StringBuilder sb;
foreach(int i in Enumerable.Range(0, 10))
{
x = i * i;
sb = new StringBuilder();
sb.Append(x);
Console.WriteLine(sb);
}
}
tôi cố tình chọn cả một giá trị kiểu và một tham chiếu kiểu trong trường hợp có ảnh hưởng đến mọi thứ. Bây giờ, IL cho họ:
.method public hidebysig instance void Method1() cil managed
{
.maxstack 2
.locals init (
[0] int32 i,
[1] int32 x,
[2] class [mscorlib]System.Text.StringBuilder sb,
[3] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> enumerator)
L_0000: ldc.i4.0
L_0001: ldc.i4.s 10
L_0003: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32, int32)
L_0008: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
L_000d: stloc.3
L_000e: br.s L_002f
L_0010: ldloc.3
L_0011: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
L_0016: stloc.0
L_0017: ldloc.0
L_0018: ldloc.0
L_0019: mul
L_001a: stloc.1
L_001b: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
L_0020: stloc.2
L_0021: ldloc.2
L_0022: ldloc.1
L_0023: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(int32)
L_0028: pop
L_0029: ldloc.2
L_002a: call void [mscorlib]System.Console::WriteLine(object)
L_002f: ldloc.3
L_0030: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
L_0035: brtrue.s L_0010
L_0037: leave.s L_0043
L_0039: ldloc.3
L_003a: brfalse.s L_0042
L_003c: ldloc.3
L_003d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0042: endfinally
L_0043: ret
.try L_000e to L_0039 finally handler L_0039 to L_0043
}
.method public hidebysig instance void Method2() cil managed
{
.maxstack 2
.locals init (
[0] int32 x,
[1] class [mscorlib]System.Text.StringBuilder sb,
[2] int32 i,
[3] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> enumerator)
L_0000: ldc.i4.0
L_0001: ldc.i4.s 10
L_0003: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> [System.Core]System.Linq.Enumerable::Range(int32, int32)
L_0008: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
L_000d: stloc.3
L_000e: br.s L_002f
L_0010: ldloc.3
L_0011: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
L_0016: stloc.2
L_0017: ldloc.2
L_0018: ldloc.2
L_0019: mul
L_001a: stloc.0
L_001b: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
L_0020: stloc.1
L_0021: ldloc.1
L_0022: ldloc.0
L_0023: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(int32)
L_0028: pop
L_0029: ldloc.1
L_002a: call void [mscorlib]System.Console::WriteLine(object)
L_002f: ldloc.3
L_0030: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
L_0035: brtrue.s L_0010
L_0037: leave.s L_0043
L_0039: ldloc.3
L_003a: brfalse.s L_0042
L_003c: ldloc.3
L_003d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0042: endfinally
L_0043: ret
.try L_000e to L_0039 finally handler L_0039 to L_0043
}
Như bạn có thể thấy, ngoài các thứ tự trên stack trình biên dịch đã xảy ra để lựa chọn - có thể cũng giống như cũng đã là một thứ tự khác nhau - nó đã hoàn toàn không có hiệu lực. Đổi lại, có thực sự không phải là bất cứ điều gì mà một là cho jitter để sử dụng nhiều mà người khác không phải là cho nó.
Ngoài ra, có một sự khác biệt về loại.
Trong Method1()
tôi, x
và sb
được scoped đến foreach
, và không thể truy cập hoặc là cố tình hay vô tình bên ngoài của nó.
Trong tôi Method2()
, x
và sb
không biết tại thời gian biên dịch được gán đáng tin cậy một giá trị trong foreach
(trình biên dịch không biết foreach
sẽ thực hiện ít nhất một vòng lặp), vì vậy sử dụng nó bị cấm .
Cho đến nay, không có sự khác biệt thực sự.
tôi thể tuy nhiên gán và sử dụng x
và/hoặc sb
ngoài foreach
. Theo quy định, tôi có thể nói rằng đây có lẽ là phạm vi nghèo nàn hầu hết thời gian, vì vậy tôi ưu tiên Method1
, nhưng tôi có thể có một số lý do hợp lý để đề cập đến chúng (thực tế hơn nếu chúng không được bỏ gán), trong trường hợp tôi muốn đi cho Method2
.
Tuy nhiên, đó là vấn đề về cách mỗi mã có thể được mở rộng hay không, không phải là sự khác biệt của mã như được viết. Thực sự, không có sự khác biệt.
Không có sự khác biệt về hiệu suất nếu bạn không ghi lại biến trong một lambda. – Jon
Bạn nên đánh giá sự khác biệt ngữ nghĩa và không phải là sự khác biệt hiệu suất, mà trong trường hợp này là không có. Bằng cách khai báo bên trong vòng lặp, bạn tự động biết nó chỉ được sử dụng bên trong vòng lặp. –
Tôi ghét làm điều đó, nhưng -1: câu hỏi của bạn không chỉ ra bất kỳ nỗ lực nghiên cứu nào. Bạn có thể chắc chắn đã thử nghiệm điều này một mình. –