Поиск

Операторы отношения

Большинство операторов возвращает числовые значения. Что касается операторов отношения, они генерируют булевский результат. Вместо / того чтобы выполнять математические операции с набором операндов, / операторы отношения анализируют соотношение между операндами и возвращают значение true, если соотношение истинно, false — если/ ложно.

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

К операторам отношения, называемым операторами сравнения, относят)-ся «меньше» (<), «меньше или равно» (<=), «больше» (>), «больше или равно» (>=), «равно» (==) и «не равно» (!=). Применение этих операторов к числам понятно, но при использовании с объектами их выполнение не так очевидно. Вот пример:

using System;
class NumericTest {
public NumericTest(int 1)
{
this.i = i;
>
protected int 1; }
class RelationalOpslApp {
public static void Main() {
NumericTest testl = new NumericTest(42);
NumericTest test2 = new NumericTest(42);

Console.WriteLine("{0}", testl == test2); > }

Если вы программируете на Java, вы знаете что здесь должно произойти. Однако С++-программисты будут скорей всего удивлены, увидев результат false. Напомню: создавая экземпляр объекта, вы получаете ссылку на него. Значит, встречая оператор отношения, сравнивающий два объекта, компилятор С# сравнивает не содержимое объектов, а их адреса. Чтобы лучше в этом разобраться, рассмотрим MSIL-код:

\ .method public hldebysig static void MainQ il managed
v
\ .entrypoint
\ // Размер кода 39 (0x27)
1 .maxstack 3
.locals (class NumericTest V_0, \ class NumericTest V_1, 1 bool V_2)
ILJJOOO: Idc.i4.s 42
1L_0002: newobj instance void NumericTest::.ctor(int32)
IL_0007: stloc.O
IL_0008: Idc.i4.s 42
IL_OOOa: newobj instance void NumericTest::.ctor(int32)
IL_OOOf: stloc.1
IL_0010: Idstr "{0}"
IL_0015: ldloc.0
IL_0016: ldloc.1
IL_0017: eeq
IL_0019: stloc.2
IL_001a: Idloca.s V_2
IL_001c: box ['mscorlib']System.Boolean
IL_0021: call void ['mscorlib']System.Console::WriteLine
(class System.String,class System.Object)
IL_0026: ret } // конец метода 'RelationalOpslApp::Main'

Посмотрите на строку .locals. Компилятор указывает, что у метода Main три локальных переменных. Первые две — объекты NumericTest, a третья — переменная булевского типа. Теперь перейдем к строкам IL_0002 и IL_0007. Здесь создается экземпляр объекта testl, и ссылка на него с помощью stloc сохраняется в первой локальной переменной. При этом важно, что MSIL сохраняет адрес вновь созданного объекта. Затем в строках IL_OOOa и IL_OOOf вы видите коды MSIL для создания объекта test2 и сохранения возвращаемой ссылки во второй локальной переменной. Наконец, в строках 1LJ)015 и IL_0016 локальные переменные помещаются в стек командой Idloc, а в строке IL_0017 команда сед сравнивает два значения в вершине стека (т. е. ссылки на объекты testl и testl). Возвращаемое значение сохраняется в третьей локальной переменной и далее выводится методом System.Console. WriteLine.

Но как же сравнивать члены двух объектов? Ответ — в использовании неявного базового класса всех объектов .NET Framework. Именно , для этих целей у класса System.Object есть метод Equals. Например, еле- / дующий код выполняет сравнение содержимого объектов и выводит, как / и следовало ожидать, true: I

using System; /
class RelationalOps2App
{ / public static void Main() {
Decimal testl = new Decimal(42); Decimal test2 = new Decimal(42);
Console.WriteLine("{0}", testl.Equals(test2));
} >

В примере RelationalOpslApp используется «самодельный» класс (Nu-mericTest), а во втором примере — .NET-класс (Decimal). Дело в том, что метод System.Object.Equals нужно переопределить, чтобы выполнить реальное сравнение членов. Следовательно, метод Equals с классом Nume-ricTest работать не будет, так как мы не переопределили метод. А вот класс Decimal переопределяет наследуемый им метод Equals, и в этом случае все будет работать.

Другой способ сравнения объектов — использование перегрузки операторов (operator overloading). Перегрузка операторов определяет операции, выполняемые над объектами конкретного типа. Например, для объектов string оператор + не выполняет сложение, а конкатенирует строки. Перегрузку операторов мы рассмотрим в главе 13.