Эффективный код с циклами и типами C # - PullRequest
0 голосов
/ 04 мая 2011


Я хотел бы узнать, что более эффективно (если даже) в следующем коде
Тип значения

ForEach(string s in strings)
{
  string t = s;
}
// or
string t;
ForEach(string s in strings)
{
   t = s;
}

и есть ли другое с ссылочными типами.

Ответы [ 8 ]

10 голосов
/ 04 мая 2011

Два фрагмента кода выдают точно один и тот же IL. Вот код C #, с которым я тестировал:

  public string[] strings = new string[5];

  public void TestOne()
  {
     foreach (string s in strings)
     {
        string t = s;
     }
  }

  public void TestTwo()
  {
     string t;
     foreach (string s in strings)
     {
        t = s;
     }
  }

А вот итоговый IL для обоих методов после компиляции с включенной оптимизацией:

.method public hidebysig instance void  TestOne() cil managed
{
  // Code size       26 (0x1a)
  .maxstack  2
  .locals init ([0] string s,
           [1] string[] CS$6$0000,
           [2] int32 CS$7$0001)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      string[] strings
  IL_0006:  stloc.1
  IL_0007:  ldc.i4.0
  IL_0008:  stloc.2
  IL_0009:  br.s       IL_0013
  IL_000b:  ldloc.1
  IL_000c:  ldloc.2
  IL_000d:  ldelem.ref
  IL_000e:  stloc.0
  IL_000f:  ldloc.2
  IL_0010:  ldc.i4.1
  IL_0011:  add
  IL_0012:  stloc.2
  IL_0013:  ldloc.2
  IL_0014:  ldloc.1
  IL_0015:  ldlen
  IL_0016:  conv.i4
  IL_0017:  blt.s      IL_000b
  IL_0019:  ret
} // end of method TestOne


.method public hidebysig instance void  TestTwo() cil managed
{
  // Code size       26 (0x1a)
  .maxstack  2
  .locals init ([0] string s,
           [1] string[] CS$6$0000,
           [2] int32 CS$7$0001)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      string[] strings
  IL_0006:  stloc.1
  IL_0007:  ldc.i4.0
  IL_0008:  stloc.2
  IL_0009:  br.s       IL_0013
  IL_000b:  ldloc.1
  IL_000c:  ldloc.2
  IL_000d:  ldelem.ref
  IL_000e:  stloc.0
  IL_000f:  ldloc.2
  IL_0010:  ldc.i4.1
  IL_0011:  add
  IL_0012:  stloc.2
  IL_0013:  ldloc.2
  IL_0014:  ldloc.1
  IL_0015:  ldlen
  IL_0016:  conv.i4
  IL_0017:  blt.s      IL_000b
  IL_0019:  ret
} // end of method TestTwo

Как обычно, правило такое же: доверяйте своему компилятору . Пусть он обрабатывает эти оптимизации для вас, а не пытается беспокоиться об этом, когда вы пишете код. Просто напишите код, который имеет смысл и доступен для чтения.

Самое главное, игнорировать всех людей, которые утверждают, что один из них "теоретически" лучше другого. Это полная чушь. Там нет теории, которая актуальна до тех пор, пока код не скомпилирован. И поскольку он компилируется в одно и то же, он гарантированно полностью идентичен по производительности.

Но если вы все еще не верите моим убеждениям доверять вашему компилятору (некоторые из нас упрямы, я знаю, потому что даже я иногда так и есть), по крайней мере, теперь вы знаете, как ответить на эти вопросы самостоятельно. Напишите пример кода, демонстрирующий оба варианта, скомпилируйте его в режиме «Release», затем откройте сборку в ILDASM.exe и сравните полученный код IL для себя. Такие вещи очень легко проверить, и нет никаких оснований угадывать.

5 голосов
/ 04 мая 2011

Ну, ни один из них не скомпилируется (я полагаю, что вы хотите использовать foreach вместо ForEach?), Но если они это сделают, они должны скомпилироваться до того же IL после оптимизации, и поэтому не будет никакой разницы.

2 голосов
/ 04 мая 2011

Вам не нужно делать это вообще.Просто используйте s.

Кроме того, string в C # / BCL является ссылочным типом.

1 голос
/ 04 мая 2011

строки неизменяемые ссылочные типы .

Итак, код

string t;
foreach(string s in strings)
{
   t = s;
}

создает новый экземпляр строки для каждого шага в перечислителе в любом случае (он не "повторно использует" экземпляр t).

Нет практического различия между двумя примерами кода, которые вы опубликовали.

1 голос
/ 04 мая 2011

Теоретически, второй должен быть более эффективным, потому что выделение памяти выполняется только один раз перед началом цикла, тогда как первый должен пытаться выделять память на каждой итерации.

Однако я не знаюесли какая-либо оптимизация выполняется компилятором на основе того, как переменная используется внутри и вне цикла (во втором случае переменная может использоваться в других частях кода, а не только в цикле).

1 голос
/ 04 мая 2011

Они оба одинаковы для эффективности памяти, потому что за кулисами все они указывают на одну и ту же строку.Это семантика, используемая в .NET / Java, и причина, по которой строки неизменны.

0 голосов
/ 04 мая 2011

Это тот же код, только объявление находится в другом месте. Вы всегда должны пытаться объявить переменные рядом с их использованием, используя наименьшую возможную область видимости.

Обратите внимание, что foreach пишется строчными буквами, как и все ключевые слова в c #.

0 голосов
/ 04 мая 2011

Держу пари, что компилятор достаточно умен, чтобы строить их точно так же.Я не думаю, что есть какая-то разница, иди на читабельность.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...