Составной оператор присваивания —
это комбинация бинарного оператора и оператора присваивания (=). Синтаксис этих операторов таков:
хор=у
где
ор —
это оператор. Заметьте, что при этом левое значение
(lvalue)
не заменяется правым
х = х
ор
у
и
lvalue
используется как база для результата операции.
Заметьте, я сказал «оказывает такой же эффект». Компилятор не переводит выражение наподобие
х +=
5 в
х
=
х + 5,
он действует логически. С особым вниманием нужно отнестись к случаям, когда
lvalue
является методом. Рассмотрим код:
using System;
class CompoundAssignmentlApp {
protected lnt[] elements;
public int[] GetArrayElementO
{
return elements;
}
CompoundAssignment1App() {
elements = new int[1];
elements[0] = 42;
}
public static void Main() {
CompoundAssignmentlApp app = new CompoundAsslgnment1App();
Console.WrlteLine("{0>", app.GetArrayElement()[0]);
app.GetArrayElement()[0] = app.GetArrayElement()[0] + 5; Console.WriteLine("{0}", app.GetArrayElement()[0]); }. }
Обратите внимание на выделенную строку — вызов метода
Compound-AssignmentlApp.GetArrayElement
и последующее изменение первого элемента — здесь я использовал синтаксис:
х = х
ор
у
Вот какой MSIL-код будет сгенерирован: // Неэффективная методика: х = х ор у.
.method public hldebyslg static void Main() 11 managed
{
.entrypolnt
// Размер кода 79 (Ox4f) .maxstack 4
.locals (class CompoundAssignmentlApp V_0)
IL_0000: newobj instance void CompoundAssignmentlApp::.ctor() IL_0005: stloc.O IL_0006: Idstr "{ОГ IL_OOOb: ldloc.0 ILJJOOc: call instance int32[]
CompoundAssignmentlApp:: GetArrayElementO
IL_0011: ldc.14.0
IL_0012: Idelema ['mscorlib']System.Int32
IL_0017: box [
1
mscorlib']System.Int32
IL_001c: call void ['mscorlib
1
]System.Console::WriteLine (class System.String, class System.Object)
IL_0021: ldloc.0
IL_0022: call instance int32[] CompoundAssignmentlApp::GetArrayElementO
IL_0027: Idc.i4.0
IL_0028: ldloc.0
IL_0029: call instance int32[] CompoundAssignmentlApp: :GetArrayElementO
IL_002e: Idc.i4.0
IL_002f: ldelem.14
IL_0030: ldc.14.5
IL_0031: add
IL_0032: stelem.14
IL_0033: Idstr "{0}"
IL_0038: ldloc.0
IL_0039: call
instance int32[] CompoundAssignmentlApp::GetArrayElement() IL_003e: ldc.14.0
IL_003f: Idelema ['mscorlib']Systera.Int32 IL_0044: box ['msoorlib']System.Int32 IL_0049: call void ['mscorlib']System.Console::WriteLine
(class System.String, class System.Object) IL_004e: ret
} // конец метода 'CompoundAssignmentlApp::Main' !
i
Посмотрите на вьщеленные строки: метод
CompoundAssignmentlApp.Get-ArrayElement
на самом деле вызывается дважды! Это по меньшей мере неэффективно, а возможно, и пагубно в зависимости от того, что еще делает этот метод.
Теперь рассмотрим другой код, в котором применен синтаксис составного оператора присваивания:
using System;
class CompoundAssignment2App {
protected int[] elements;
public int[] GetArrayElementO
{
return elements;
}
CompoundAssignment2App() {
elements = new int[1];
elements[0] = 42;
}
public static void Main() {
CompoundAssignment2App app = new CompoundAssignment2App();
Console.WriteLine("{0}", app.GetArrayElement()[0]);
app.GetArrayElement()[0] += 5; Console.WriteLine("{0}", app.GetArrayElement()[0]); } }
Использование составного оператора присваивания приведет к созданию гораздо более эффективного MSIL-кода:
// Более эффективная методика: х ор= у.
.method public hidebysig static void Main() il managed
\
{
\ .entrypoint
I // Размер кода 76 (Ox4c) \ .maxstack 4
.locals (class CompoundAssignmentlApp V_0, int32[] V_1)
\
IL_0000: newobj instance void CompoundAssignmentlApp::.ctor()
\
IL_0005: stloc.O 1 IL_0006: Idstr "{0}" 1 IL_OOOb: ldloc.0 IL_OOOc: call instance int32[]
CompoundAssignmentlApp:: GetArrayElementO IL_0011: Idc.i4.0
IL_0012: Idelema [•mscorlib']System.Int32 IL_0017: box [
>
mscorlib
-
]System.Int32 lL_001c: call void ['mscorlib']System.Console::WriteLine
(class System.String, class System.Object) IL_0021: ldloc.0 IL_0022: call instance int32[]
CompoundAssignmentlApp::GetArrayElement()
IL_0027: dup
IL_0028: stloc.1
IL_0029: Idc.i4.0
IL_002a: ldloc.1
IL_002b: Idc.i4.0
IL_002c: ldelem.14
IL_002d: ldc.14.5
IL_002e: add
IL_002f: stelem.14
IL_0030: Idstr "{0}"
IL_0035: ldloc.0
IL_0036: call instance int32[]
CompoundAssignmentlApp:: GetArrayElementO IL_003b: Idc.i4.0
IL_003c: Idelema ['mscorlib']System.Int32
IL_0041: box [•mscorlib']System.Int32
IL_0046: call void ['mscorlib']System.Console::WriteLine
(class System.String, class System.Object)
IL_004b: ret } // конец метода 'CompoundAssignmentlApp::Main'
Вы видите, что использована команда MSIL
dup.
Она дублирует верхний элемент в стеке, создавая таким образом копию значения, возвра-щенного методом
CompoundAssignmentlApp.Get Array Element. I
Из этого видно, что хотя по сути
х +=у
эквивалентно
х = х + у,
MSIL- код в обоих случаях разный. Это отличие должно заставить вас задуматься, какой синтаксис использовать в каждом отдельном случае. Эмпирическое правило и моя рекомендация: всегда и везде, где можно, применять составные операторы присваивания.