Примечание: Обратите внимание, что приведенный ниже код по сути бессмысленен и предназначен только для целей иллюстрации.
] Основываясь на том факте, что правая часть присваивания всегда должна оцениваться, прежде чем ее значение будет присвоено левой переменной, и что операции увеличения, такие как ++
и - -
всегда выполняются сразу после оценки, я бы не ожидал, что следующий код будет работать:
string[] newArray1 = new[] {"1", "2", "3", "4"};
string[] newArray2 = new string[4];
int IndTmp = 0;
foreach (string TmpString in newArray1)
{
newArray2[IndTmp] = newArray1[IndTmp++];
}
Скорее, я ожидал бы, что newArray1 [0]
будет назначен на newArray2 [1]
, newArray1 [1]
до newArray [2]
и так далее, вплоть до выброса исключения System.IndexOutOfBoundsException
. Вместо этого, к моему большому удивлению, версия, которая вызывает исключение, - это
string[] newArray1 = new[] {"1", "2", "3", "4"};
string[] newArray2 = new string[4];
int IndTmp = 0;
foreach (string TmpString in newArray1)
{
newArray2[IndTmp++] = newArray1[IndTmp];
}
Поскольку, как я понимаю, компилятор сначала оценивает RHS,назначает его LHS и только потом увеличивает это для меня неожиданное поведение. Или это действительно ожидаемо, и мне явно чего-то не хватает?
newArray2[IndTmp] = newArray1[IndTmp++];
приводит к сначала присвоению, а затем увеличению переменной.
и т. Д.
Оператор RHS ++ сразу увеличивается, но возвращает значение до его увеличения. Значение, используемое для индексации в массиве, является значением, возвращаемым оператором RHS ++, поэтому это значение не увеличивается.
То, что вы описываете (сгенерированное исключение), будет результатом LHS ++:
newArray2[IndTmp] = newArray1[++IndTmp]; //throws exception
ILDasm иногда может быть вашим лучшим другом; -)
Я собрал оба ваших метода и сравнил итоговый IL (ассемблер).
Важная деталь находится в цикле, что неудивительно. Ваш первый метод компилируется и запускается так:
Code Description Stack
ldloc.1 Load ref to newArray2 newArray2
ldloc.2 Load value of IndTmp newArray2,0
ldloc.0 Load ref to newArray1 newArray2,0,newArray1
ldloc.2 Load value of IndTmp newArray2,0,newArray1,0
dup Duplicate top of stack newArray2,0,newArray1,0,0
ldc.i4.1 Load 1 newArray2,0,newArray1,0,0,1
add Add top 2 values on stack newArray2,0,newArray1,0,1
stloc.2 Update IndTmp newArray2,0,newArray1,0 <-- IndTmp is 1
ldelem.ref Load array element newArray2,0,"1"
stelem.ref Store array element <empty>
<-- newArray2[0] = "1"
Это повторяется для каждого элемента в newArray1. Важным моментом является то, что расположение элемента в исходном массиве было помещено в стек до увеличения IndTmp.
Сравните это со вторым методом:
Code Description Stack
ldloc.1 Load ref to newArray2 newArray2
ldloc.2 Load value of IndTmp newArray2,0
dup Duplicate top of stack newArray2,0,0
ldc.i4.1 Load 1 newArray2,0,0,1
add Add top 2 values on stack newArray2,0,1
stloc.2 Update IndTmp newArray2,0 <-- IndTmp is 1
ldloc.0 Load ref to newArray1 newArray2,0,newArray1
ldloc.2 Load value of IndTmp newArray2,0,newArray1,1
ldelem.ref Load array element newArray2,0,"2"
stelem.ref Store array element <empty>
<-- newArray2[0] = "2"
Здесь IndTmp увеличивается до того, как местоположение элемента в исходном массиве было помещено в стек, отсюда и разница в поведении (и последующие исключение).
Для полноты давайте сравним его с
newArray2[IndTmp] = newArray1[++IndTmp];
Code Description Stack
ldloc.1 Load ref to newArray2 newArray2
ldloc.2 Load IndTmp newArray2,0
ldloc.0 Load ref to newArray1 newArray2,0,newArray1
ldloc.2 Load IndTmp newArray2,0,newArray1,0
ldc.i4.1 Load 1 newArray2,0,newArray1,0,1
add Add top 2 values on stack newArray2,0,newArray1,1
dup Duplicate top stack entry newArray2,0,newArray1,1,1
stloc.2 Update IndTmp newArray2,0,newArray1,1 <-- IndTmp is 1
ldelem.ref Load array element newArray2,0,"2"
stelem.ref Store array element <empty>
<-- newArray2[0] = "2"
Здесь результат приращения был помещен в стек (и становится индексом массива) перед обновлением IndTmp.
Подводя итог, представляется, что сначала оценивается цель назначения, а затем источник .
Большие пальцы руки к ОП для действительно провокационного вопроса!
Он генерирует исключение, потому что вы начинаете индексирование в newArray1
по индексу 1. Поскольку вы выполняете итерацию по каждому элементу в newArray1
, последнее присваивание выдает исключение, потому что IndTmp
равно newArray1.Length
, т.е. за концом массива. Вы увеличиваете индексную переменную до того, как она когда-либо использовалась для извлечения элемента из newArray1
, что означает, что вы потерпите крах, а также пропустите первый элемент в newArray1
.