Принципы объектно-ориентированного программирования

         

Составные операторы присваивания



Составные операторы присваивания

Составной оператор присваивания — это комбинация бинарного оператора и оператора присваивания (=). Синтаксис этих операторов таков:

хор=у

где ор — это оператор. Заметьте, что при этом левое значение (lvalue) не заменяется правым


(rvalue), составной оператор оказывает такой же эффект, как:

х = х ор у

и 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- код в обоих случаях разный. Это отличие должно заставить вас задуматься, какой синтаксис использовать в каждом отдельном случае. Эмпирическое правило и моя рекомендация: всегда и везде, где можно, применять составные операторы присваивания.



Содержание раздела