Выражения
В обоих языках выражения строятся при помощи применения операторов к именам и литералам. Условно можно считать, что имеется следующий общий набор операторов:
- 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;
- =, *=, /=, %=, +=, -=, <<=, >>=, &=, |=, ^= — бинарные операторы присваивания, все они, кроме первого, сначала производят некоторую операцию над старым значением левого операнда и значением правого, а затем присваивают полученный результат левому операнду.
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]. |