Использует ли один из них больше ресурсов, чем другой?
Что происходит в фоновом режиме для этих двух кодовых блоков? Будет ли считаться "лучше", чем другой?
Моя мысль заключается в том, что Example2 может быть хуже, потому что ему может потребоваться уничтожить сборщик мусора, но я не знаю достаточно о сборщике мусора, чтобы узнать, правда ли это.
Пример1:
ListItem item;
for (int i = 1; i <= 32; i++)
{
item = new ListItem();
//do some stuff
}
Пример 2:
for (int i = 1; i <= 32; i++)
{
ListItem item = new ListItem();
//do some stuff
}
Ответы
Ответ 1
Я скопировал ваш код в Visual Studio, скомпилировал его, а затем посмотрел на сгенерированный IL. Это IL, созданный в примере 1:
.method private hidebysig static void One() cil managed
{
// Code size 30 (0x1e)
.maxstack 2
.locals init ([0] class WinTest.ListItem item,
[1] int32 i,
[2] bool CS$4$0000)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.1
IL_0003: br.s IL_0011
IL_0005: nop
IL_0006: newobj instance void WinTest.ListItem::.ctor()
IL_000b: stloc.0
IL_000c: nop
IL_000d: ldloc.1
IL_000e: ldc.i4.1
IL_000f: add
IL_0010: stloc.1
IL_0011: ldloc.1
IL_0012: ldc.i4.s 32
IL_0014: cgt
IL_0016: ldc.i4.0
IL_0017: ceq
IL_0019: stloc.2
IL_001a: ldloc.2
IL_001b: brtrue.s IL_0005
IL_001d: ret
} // end of method Program::One
И это IL, сгенерированный из примера 2:
.method private hidebysig static void Two() cil managed
{
// Code size 30 (0x1e)
.maxstack 2
.locals init ([0] int32 i,
[1] class WinTest.ListItem item,
[2] bool CS$4$0000)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: br.s IL_0011
IL_0005: nop
IL_0006: newobj instance void WinTest.ListItem::.ctor()
IL_000b: stloc.1
IL_000c: nop
IL_000d: ldloc.0
IL_000e: ldc.i4.1
IL_000f: add
IL_0010: stloc.0
IL_0011: ldloc.0
IL_0012: ldc.i4.s 32
IL_0014: cgt
IL_0016: ldc.i4.0
IL_0017: ceq
IL_0019: stloc.2
IL_001a: ldloc.2
IL_001b: brtrue.s IL_0005
IL_001d: ret
} // end of method Program::Two
Насколько я понимаю, они идентичны, за исключением того, что локальные жители объявляются (и, следовательно, обращаются) в обратном порядке. Я не ожидаю, что это повлияет на производительность.
Ответ 2
Это зависит от того, что означает "//делать некоторые вещи".
В простой программе оба примера будут скомпилированы с одним и тем же байтовым кодом MSIL.
Но если в цикле создается анонимный делегат, возможно, выполняется в другом потоке, который ссылается на переменную "item", имеет значение, был ли объявлен "элемент" внутри или вне цикла. Если, как в примере 2, "элемент" объявляется внутри цикла, тогда при запуске делегата будет видно значение "item", назначенное в итерации цикла, который создал делегат (что наиболее вероятно, что предназначено для эти случаи). Если, как в примере 1, "элемент" был объявлен вне цикла, тогда делегат увидит значение, назначенное во время его выполнения, которое может быть с более поздней итерации, чем тот, который создал делегат. Это приводит к запутыванию условий гонки.
В принципе, хотя байт-код MSIL, который компиляция С# не представляет область видимости переменной, область действия имеет смысл в С# и может влиять на то, как ведут себя различные синтаксические сахара.
Ответ 3
Удивительно, но мои тесты показывают пример 2 быстрее.
Выход. Случай 1 представляет собой единую декларацию. Случай 2 является объявлением цикла.
Case 1: 10.280418100
Case 2: 10.264818000 99.848254226%
Case 1: 10.592418600
Case 2: 10.140017800 95.729013202%
Case 1: 10.233618000
Case 2: 10.108817800 98.780487996%
Case 1: 10.155617800
Case 2: 10.046417600 98.924731098%
Case 1: 10.503818600
Case 2: 10.246319800 97.548522020%
Case 1: 10.243018400
Case 2: 10.030817600 97.928337217%
Case 1: 10.077617700
Case 2: 10.218017900 101.393188392%
Case 1: 10.303019300
Case 2: 10.526318800 102.167320991%
Case 1: 10.353619900
Case 2: 10.276219400 99.252430544%
Case 1: 10.264818100
Case 2: 10.202417900 99.392096388%
Case 1 Total: 103.007984500
Case 2 Total: 102.060182600 99.079875308%
код:
Public Sub Main()
Dim Case1Total As Double = 0
Dim Case2Total As Double = 0
For i As Integer = 1 To 10
Dim Case1 As Double = MeasureTime(AddressOf Case1Method).TotalSeconds
Case1Total += Case1
Console.WriteLine("Case 1: {0:N9}", Case1)
Dim Case2 As Double = MeasureTime(AddressOf Case2Method).TotalSeconds
Case2Total += Case2
Console.WriteLine("Case 2: {0:N9} {1:N9}%", Case2, 100 * Case2 / Case1)
Next i
Console.WriteLine()
Console.WriteLine("Case 1 Total: {0:N9}", Case1Total)
Console.WriteLine("Case 2 Total: {0:N9} {1:N9}%", Case2Total, 100 * Case2Total / Case1Total)
Console.ReadLine()
End Sub
Private Function MeasureTime(Method As Action) As TimeSpan
Dim StartTime As Date = Date.Now
Method()
Return Date.Now - StartTime
End Function
Private Sub Case1Method()
Dim o As Label
For i As Integer = 0 To Limit
o = New Label
o.Text = "Label" & i.ToString
o.Dispose()
Next
End Sub
Private Sub Case2Method()
For i As Integer = 0 To Limit
Dim o As New Label
o.Text = "Label" & i.ToString
o.Dispose()
Next
End Sub
Private Const Limit As Integer = 1024 * 1024
Ответ 4
Мое личное чувство было бы в том, что здесь будет небольшая разница (за пределами очевидной разницы в показаниях).
Любой из них выделит новую память с ссылкой на нее и оставит предыдущую память с меньшей ссылкой, готов к GC'd как нужно, если потребуется.
Я не думаю, что 2 будет ждать GC, отчасти потому, что GC не является "встроенным", а отчасти потому, что нет гарантии, что item
является единственной ссылкой на экземпляр и требует GC в любом случае - как Я говорю, что счетчик ссылок будет уменьшен и что он.
Конечно, это просто мое чувство об этом, и мне будет интересно узнать, что другие скажут (я могу почувствовать несколько сообщений в блоге, возможно, в письме!)
Ответ 5
Когда я сохранил //do some stuff
empty, обе версии генерировали точно такой же IL, поэтому там не может быть никакой возможной разницы. И я думаю, что если вы поместили там какой-то код, сгенерированный IL все равно будет таким же.
Что касается GC, второй пример может выглядеть так, как это может быть теоретически быстрее, потому что GC может собрать ListItem
до выполнения приращения и сравнения цикла. Но на самом деле, он может сделать то же самое и в первом случае, поэтому нет никакой разницы.
Единственное исключение, когда две версии не эквивалентны, - это использование закрытий. В первой версии есть только одна переменная "экземпляр", которая закрыта, во втором случае их 32. Таким образом, существует разница в производительности (во втором случае объект закрытия создается для каждой итерации), но разница в значении гораздо важнее.
В общем, мне нравится держать переменные настолько глубокими, насколько это возможно, потому что я думаю, что это помогает читать.