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