Оператор логического отрицания
Для операции вида !x чтобы выбрать конкретную реализацию оператора, применяется разрешение перегрузки унарного оператора (§7.3.3). Операнд преобразуется в тип параметра выбранного оператора, а тип результата является типом возвращаемого значения этого оператора. Существует единственный стандартный оператор логического отрицания:
bool operator !(bool x);
Этот оператор вычисляет логическое отрицание операнда. Если операнд равен true, то результат равен false. Если значение операнда равно false, результат равен true.
Оператор побитового дополнения
Для операции вида ~x чтобы выбрать конкретную реализацию оператора, применяется разрешение перегрузки унарного оператора (§7.3.3). Операнд преобразуется в тип параметра выбранного оператора, а тип результата является типом возвращаемого значения этого оператора. К стандартным операторам побитового дополнения относятся:
int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);
Для каждого из этих операторов результатов операции является побитовое дополнение x.
Каждый тип перечисления E неявно предоставляет следующий оператор побитового дополнения:
E operator ~(E x);
Результат вычисления x, где x является выражением перечисляемого типа E с базовым типом U, в точности равен значению (E)(~(U)x), за исключением того, что преобразование в E всегда выполняется как в контексте unchecked (§7.6.12).
Префиксные операторы инкремента и декремента
pre-increment-expression:
++ unary-expression
pre-decrement-expression:
-- unary-expression
Операнд префиксного оператора инкремента или декремента должен быть выражением, которое классифицируется как переменная, доступ к свойству или доступ к индексатору. Результатом операции является значение того же типа, что и операнд.
Если операндом префиксного оператора инкремента или декремента является свойство или доступ к индексатору, то у свойства и индексатора должны быть оба метода доступа get и set. В противном случае возникает ошибка времени привязки.
Для выбора конкретной реализации оператора используется разрешение перегрузки унарных операторов (§7.3.3). Предопределенные операторы ++ и -- существуют для следующих типов: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal и любых перечисляемых типов. Стандартный оператор ++ возвращает значение, полученное добавлением 1 к операнду, а стандартный оператор -- возвращает значение, полученное вычитанием 1 из операнда. В контексте checked если результат такого сложения или вычитания выходит за пределы допустимого диапазона для типа результата и результат имеет целый тип или тип перечисления, то возникает исключение System.OverflowException.
Во время выполнения обработка префиксных операций инкремента или декремента в виде ++x или --x включает следующие этапы.
· Если x классифицируется как переменная, то:
o x вычисляется для создания переменной.
o Вызывается выбранный оператор со значением x в качестве аргумента.
o Значение, возвращенное оператором, сохраняется в расположении, предоставленном при вычислении x.
o Значение, возвращенное оператором, становится результатом операции.
· Если x классифицируется как свойство или доступ к индексатору, то:
o Вычисляются выражение экземпляра (если x не имеет тип static) и список аргументов (если x является доступом к индексатору), связанные с x, и полученные результаты используются при последующих вызовах методов доступа get и set.
o Вызывается метод доступа get для x.
o Вызывается выбранный оператор со значением аргумента, равным возвращенному значению метода доступа get.
o Вызывается метод доступа set для x со значением, возвращенным оператором в качестве своего аргумента value.
o Значение, возвращенное оператором, становится результатом операции.
Операторы ++ и --- также могут использоваться в постпозиции (§7.6.9). Обычно результатом операторов x++ и x-- является значение x до операции, тогда как результатом ++x и --x является значение x после операции. В обоих случаях сама переменная x имеет одинаковое значение после операции.
Реализацию operator ++ или operator -- можно вызывать в префиксной и постфиксной форме. Для двух этих форм нельзя создать разные реализации операторов.
Выражения приведения типа
Выражение приведения типа используется для явного преобразования выражения в данный тип.
cast-expression:
( type ) unary-expression
Выражение приведения типа вида (T)E, где T является типом, а E — унарным выражением, выполняет явное преобразование (§6.2) значения E в тип T. Если явное преобразование из E в T отсутствует, то возникает ошибка времени привязки. Иначе результатом является значение, полученное при явном преобразовании. Результат всегда классифицируется как значение, даже если E обозначает переменную.
Запись выражения приведения типа создает некоторую синтаксическую неоднозначность. Например, выражение (x)–y можно интерпретировать как выражение приведения типа (приведение –y к типу x) или как аддитивное выражение с выражением в скобках (в котором вычисляется значение x – y).
Для разрешения неоднозначности в выражении приведения типа существует следующее правило. Последовательность из одной или нескольких лексем (§2.3.3), заключенная в круглые скобки, считается началом выражения приведения типа, только если верно одно из следующих условий:
· Последовательность лексем имеет правильную грамматику для типа, но не для выражения.
· Последовательность лексем имеет правильную грамматику для типа, и лексема, непосредственно следующая за закрывающей скобкой, равна «~», «!», «(», идентификатору (§2.4.1), литералу (§2.4.4) или любому ключевому слову (§2.4.3) за исключением as и is.
Термин «правильная грамматика» выше означает только то, что последовательность лексем должна соответствовать конкретному грамматическому выводу. В нем специально не учитывается фактическое значение каких-либо составляющих идентификаторов. Например, если x и y являются идентификаторами, то выражение x.y имеет правильную грамматику для типа, даже если x.y фактически не означает тип.
Из правила устранения неоднозначности следует, что если x и y являются идентификаторами, то (x)y, (x)(y) и (x)(-y) являются выражениями приведения типа, а (x)-y не является таким выражением, даже если x обозначает тип. Однако если x является ключевым словом, которое обозначает стандартный тип (например, int), то все четыре вида выражения являются выражениями приведения типа (потому что такое ключевое слово не может само быть выражением).
Выражения await
Оператор await используется для приостановки вычисления содержащей его асинхронной функции до завершения асинхронной операции, представленной операндом.
await-expression:
await unary-expression
Выражения await можно использовать только в теле асинхронной функции (§10.14). В рамках ближайшей содержащей его асинхронной функции выражение await не может находится в следующих местах:
· внутри вложенной (не являющейся асинхронной) анонимной функции;
· в блоке catch или finally оператора try;
· внутри блока операторов lock;
· в небезопасном контексте.
Обратите внимание, что выражение await не может находится в большинстве мест внутри выражения запроса, поскольку эти выражения синтаксически преобразуются для использования лямбда-выражений, не являющихся асинхронными.
Внутри асинхронной функции await нельзя использовать как идентификатор. Это позволяет устранить все синтаксические неоднозначности между выражениями await и различными выражениями, использующими идентификаторы. За пределами асинхронных функций await работает как обычный идентификатор.
Операнд выражения await называется задачей. Он представляет асинхронную операцию, которая может быть завершена или не завершена во время вычисления выражения await. Оператор await предназначен для приостановки выполнения содержащей его асинхронной функции до завершения ожидаемой задачи и получения ее результата.
Выражения с ожиданием
Задача выражения await должна быть ожидаемым объектом. Выражение t является ожидаемым, если выполняется одно из следующих условий.
· t is of compile time type dynamic
· t has an accessible instance or extension method called GetAwaiter with no parameters and no type parameters, and a return type A for which all of the following hold:
o A реализует интерфейс System.Runtime.CompilerServices.INotifyCompletion (в дальнейшем для краткости называемый INotifyCompletion).
o A имеет доступное для чтения свойство экземпляра IsCompleted типа bool.
o A имеет доступный метод экземпляра GetResult без параметров и параметров типа.
Метод GetAwaiter предназначен для получения ожидающего объекта для задачи. Тип A называется типом ожидающего объекта для выражения await.
С помощью свойства IsCompleted можно определить, завершена ли задача. Если она завершена, нет смысла приостанавливать вычисление.
Метод INotifyCompletion.OnCompleted необходим для того, чтобы подписаться на "продолжение" задачи, то есть на делегат (типа System.Action), который будет вызван после завершения задачи.
С помощью метода GetResult можно получить результат задачи после ее завершения. Результатом может быть успешное завершение, иногда с результирующим значением, или исключение, создаваемое методом GetResult.