Компонентный подход в программировании

Выражения


В обоих языках выражения строятся при помощи применения операторов к именам и литералам. Условно можно считать, что имеется следующий общий набор операторов:

  • x.y — оператор уточнения имени, служит для получения ссылки на элемент пространства имен или типа, либо для получения значения поля (или свойства в C#);
  • f(x) — оператор вызова метода (а также делегата в C#) с заданным набором аргументов;
  • a[x] — оператор вычисления элемента массива (а также обращения к индексеру в C#);
  • new — оператор создания нового объекта (или значения в C#), используется вместе с обращением к одному из конструкторов типа — new MyType("Yes", 2) (в Java с его помощью нельзя создавать значения примитивных типов);
  • ++, -- — префиксные и постфиксные унарные операторы увеличения/уменьшения на 1;
  • (T)x — оператор явного приведения к типу T;
  • +, - — унарные операторы сохранения/изменения знака числа;
  • ! — унарный оператор логического отрицания;
  • ~ — унарный оператор побитового отрицания;
  • *, /, %, +, - — бинарные операторы умножения, деления, взятия остатка по модулю, сложения и вычитания;
  • <<, >> — бинарные операторы побитовых сдвигов влево/вправо;
  • <, >, <=, >= — бинарные операторы сравнения по порядку;
  • ==, != — бинарные операторы сравнения на равенство/неравенство;
  • &, |, ^ — бинарные операторы логических или побитовых операций: конъюнкции, дизъюнкции, сложения по модулю 2;
  • &&, || — бинарные операторы условных конъюнкции и дизъюнкции, (x && y) эквивалентно (x?y:false), a (x || y) — (x?true:y);
  • ?: — тернарный условный оператор, выражение a?x:y вычисляет значение a, если оно true, то вычисляется и возвращается значение x, иначе вычисляется и возвращается значение y;
  • =, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^= — бинарные операторы присваивания, все они, кроме первого, сначала производят некоторую операцию над старым значением левого операнда и значением правого, а затем присваивают полученный результат левому операнду.

Таблица 10.1. Приоритет и ассоциативность операторов

ОператорыАссоциативность
x.y, f(x), a[x], new, x++, x--
+, -, !, ~, ++x, --x, (T)x
*, /, % левая
+, - левая
<<, >> левая
<, >, <=, >= левая
==, != левая
& левая
^ левая
| левая
&& левая
|| левая
?: правая
=, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^= правая


В табл. 10. 1 операторы перечисляются сверху вниз в порядке уменьшения их приоритета, а также приводится ассоциативность всех операторов. Оператор op называется левоассоциативным, если выражение (x op y op z) трактуется компилятором как ((x op y) op z), и правоассоциативным, если оно трактуется как (x op (y op z)).

Помимо перечисленных выше операторов имеются также общие для обоих языков операции, которые выполняются при помощи различных конструкций — это получение объекта, представляющего тип, который задан по имени, и проверка принадлежности объекта или значения типу. В каждом из языков есть также несколько операторов, специфических для данного языка.

Получение объекта, представляющего тип, связано с механизмом рефлексии (reflection), имеющимся в обоих языках. Этот механизм обеспечивает отображение сущностей языка (типов, операций над ними, полей их данных и пр.) в объекты самого языка. В обоих языках операция получения объекта, представляющего тип, входит в группу операций с высшим приоритетом.



Любой тип Java однозначно соответствует некоторому объекту класса java.lang.Class, любой метод описывается с помощью одного из объектов класса java.lang.reflect.Method, любое поле — с помощью одного из объектов класса java.lang.reflect.Field.

Получить объект типа Class, представляющий тип T (даже если T = void), можно с помощью конструкции T.class.


В C# типы представляются объектами класса System.Type, методы — объектами System.Reflection.MethodInfo, а поля — объектами System.Reflection.FieldInfo.

Объект типа System.Type, представляющий тип T, можно получить при помощи конструкции typeof(T).


Для проверки того, что выражение x имеет тип T, в Java используется конструкция

(x instanceof T),

возвращающая значение логического типа.

В обоих языках операция проверки типа имеет такой же приоритет, как операторы <, >, <=, >=.


Для проверки того, что выражение x имеет тип T, в C# используется конструкция

(x is T),

имеющая логический тип.



Эта проверка использует естественные преобразования типов (подтипа в более общий тип или наоборот, если точный тип объекта является подтипом T) и автоупаковку/распаковку, не затрагивая определенных пользователем неявных преобразований.


В C# имеется и другой оператор, связанный с преобразованием типа.

Для преобразования объекта x к заданному ссылочному типу T можно использовать конструкцию

(x as T),

тип результата которой — T.

Если в результате естественных преобразований типов и автоупаковки/распаковки, значение x не преобразуется к типу T, то результат этого выражения — null.

Приоритет этого оператора такой же, как у оператора is, а ассоциативность — левая.


В Java есть дополнительный оператор сдвига числового значения вправо >>>, заполняющий освобождающиеся слева биты нулями.

Он имеет такой же приоритет, как и остальные операторы сдвига, и левую ассоциативность.

Соответствующий оператор присваивания >>>= имеет такой же приоритет, как и другие операторы присваивания, и правую ассоциативность.

В C# можно строить выражения, в рамках которых переполнения при арифметических действиях вызывают (или не вызывают) исключения при помощи оператора

checked(x) (unchecked(x)),

где x — выражение, контекст вычисления которого мы хотим определить (см. раздел о целочисленных типах).

Оба этих оператора входят в группу операторов с высшим приоритетом.


Выражение default(T) используется в C# 2.0 для получения значения типа T по умолчанию.

Для ссылочных типов это null, для числовых типов — 0, для логического типа — false, a для остальных типов значений определяется на основе их структуры.

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

Этот оператор входит в группу с высшим приоритетом.


Оператор ?? (null coalescing operator) используется в C# 2.0 в следующем смысле. Выражение (x??y) эквивалентно ((x == null)?y:x), только значение x вычисляется однократно, т.е., если значение x не равно null, то результатом этой операции является x, а иначе y.

Этот оператор имеет приоритет меньший, чем приоритет условной дизъюнкции ||, но больший, чем приоритет условного оператора ?:. Он правоассоциативен.


В C# 2.0 введен дополнительный оператор :: для разрешения контекста имен в рамках глобального пространства имен или определенных синонимов.

Дело в том, что в C# при разрешении имен, построенных с помощью точек, разделяющих идентификаторы, возможны многочисленные проблемы, связанные с непредвиденными модификациями библиотек.

Например, если мы написали директиву using System.IO;, чтобы использовать класс FileStream с коротким именем, и одновременно определяем в этом контексте класс EmptyStream, то, если в будущем в System.IO появится класс EmptyStream, полученный код перестанет компилироваться.

Эту ситуацию можно разрешить при помощи синонимов, определив, например, для System.IO синоним SIO, а для нашей собственной библиотеки, куда входит EmptyStream, синоним MIO, и используя имена классов только вместе с синонимами — SIO.FileStream, MIO.EmptyStream. Однако если в одной из используемых библиотек будет введено пространство имен MIO, проблема возникнет вновь.

Чтобы однозначно отделить типы из внешних библиотек от внутрипрограммных, можно использовать оператор ::. При этом левый аргумент такого оператора может иметь два вида. Либо он имеет значение global — тогда имя, заданное правым аргументом, ищется в глобальном пространстве имен и конфликтует с внутренними именами. Либо он является именем синонима — тогда имя, заданное правым аргументом, ищется только в пространстве имен, определяемом этим синонимом.

Этот оператор входит в группу с высшим приоритетом.


Тип или часть его операций, или даже отдельный блок в C# могут быть помечены модификатором unsafe. При этом содержимое помеченного типа, операции или блока попадает в небезопасный контекст (unsafe context). В рамках небезопасного контекста можно использовать указатели и операции над указателями, в частности, доступ к элементу данных по указателю с помощью оператора ->, построение указателей на данные и разыменование указателей, арифметические действия над указателями.

В данном курсе мы не будем больше касаться правил написания небезопасного кода в C#, предоставляя заинтересованному читателю самому разобраться в них с помощью [8].

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