Операторы и знаки пунктуации
Существует несколько видов операторов и знаков пунктуации. Операторы используются в выражениях для описания операций, включающих один или более операндов. Например, в выражении a + b используется оператор + для сложения двух операндов a и b. Знаки пунктуации служат для группирования и разделения.
operator-or-punctuator: one of
{ } [ ] ( ) . , : ;
+ - * / % & | ^ ! ~
= < > ? ?? :: ++ -- && ||
-> == != <= >= += -= *= /= %=
&= |= ^= << <<= =>
right-shift:
>|>
right-shift-assignment:
>|>=
Вертикальная черта в порождениях сдвиг вправо и присваивание сдвига вправо указывает, что, в отличие от других порождений в синтаксической грамматике, никакие символы (даже пробелы) не допустимы между этими лексемами. Эти порождения обрабатываются особо, чтобы сделать возможной правильную обработку списков параметров типа (§10.1.3).
Препроцессорные директивы
Препроцессорные директивы дают возможность условно пропускать разделы исходных файлов, сообщать об условиях ошибок и предупреждений и устанавливать отдельные области исходного кода. Термин «препроцессорные директивы» используется только для сохранения единообразия с языками программирования C и C++. В C# нет отдельного шага предварительной обработки; препроцессорные директивы обрабатываются как часть этапа лексического анализа.
pp-directive:
pp-declaration
pp-conditional
pp-line
pp-diagnostic
pp-region
pp-pragma
Доступны следующие препроцессорные директивы:
· #define и #undef, используемые, соответственно, для определения и отмены определения символов условной компиляции (§2.5.3);
· #if, #elif, #else и #endif, используемые для условного пропуска разделов исходного кода (§2.5.4);
· #line, используемая для управления номерами строк, выдаваемыми для ошибок и предупреждений (§2.5.7);
· #error и #warning, используемые для выдачи, соответственно, ошибок и предупреждений (§2.5.5);
· #region и #endregion, используемые для явного выделения разделов исходного кода (§2.5.6);
· #pragma, используемая для задания компилятору необязательных сведений о контексте (§2.5.8).
Препроцессорная директива всегда занимает отдельную строку исходного кода и всегда начинается с символа # и имени препроцессорной директивы. Пробел может стоять перед символом # и между символом # и именем директивы.
Строка исходного кода, содержащая директиву #define, #undef, #if, #elif, #else, #endif, #line или #endregion, может оканчиваться однострочным комментарием. Комментарии с разделителями (вид комментариев с /* */) не разрешены в исходных строках, содержащих препроцессорные директивы.
Препроцессорные директивы не являются лексемами и не являются частью синтаксической грамматики C#. Тем не менее, препроцессорные директивы можно использовать для включения или исключения последовательностей лексем и таким образом влиять на значение программы C#. Например, скомпилированная программа:
#define A
#undef B
class C
{
#if A
void F() {}
#else
void G() {}
#endif
#if B
void H() {}
#else
void I() {}
#endif
}
приведет к той же последовательности лексем, что и программа:
class C
{
void F() {}
void I() {}
}
Таким образом, несмотря на то, что лексически эти программы различаются, синтаксически они идентичны.
Символы условной компиляции
Функциональность условной компиляции, предоставляемая директивами #if, #elif, #else и #endif, управляется посредством препроцессорных выражений (§2.5.2) и символов условной компиляции.
conditional-symbol:
Any identifier-or-keyword except true or false
У символа условной компиляции есть два возможных состояния: определенное и неопределенное. В начале лексической обработки исходного файла символ условной компиляции не определен, если только он явным образом не определен внешним механизмом (таким как параметр командной строки компилятора). При обработке директивы #define символ условной компиляции, упомянутый в этой директиве, становится определенным в этом исходном файле. Этот символ остается определенным, пока не будет обработана директива #undef для того же самого символа, или пока не будет достигнут конец исходного файла. Это означает, что директивы #define и #undef в одном исходном файле не влияют на другие исходные файлы той же программы.
При ссылке в препроцессорном выражении определенный символ условной компиляции имеет логическое значение true, а неопределенный — логическое значение false. Символы условной компиляции не обязательно должны быть явно объявлены, чтобы на них можно было ссылаться в препроцессорных выражениях. Необъявленные символы просто являются неопределенными и имеют, таким образом, значение false.
Пространство имен для символов условной компиляции отличается и является отдельным от всех других именованных сущностей в программе на C#. На символы условной компиляции можно ссылаться только в директивах #define и #undef и в препроцессорных выражениях.
Препроцессорные выражения
Препроцессорные выражения могут быть в директивах #if и #elif. В препроцессорных выражениях разрешены операторы !, ==, !=, && и ||, для группирования можно использовать скобки.
pp-expression:
whitespaceopt pp-or-expression whitespaceopt
pp-or-expression:
pp-and-expression
pp-or-expression whitespaceopt || whitespaceopt pp-and-expression
pp-and-expression:
pp-equality-expression
pp-and-expression whitespaceopt && whitespaceopt pp-equality-expression
pp-equality-expression:
pp-unary-expression
pp-equality-expression whitespaceopt == whitespaceopt pp-unary-expression
pp-equality-expression whitespaceopt != whitespaceopt pp-unary-expression
pp-unary-expression:
pp-primary-expression
! whitespaceopt pp-unary-expression
pp-primary-expression:
true
false
conditional-symbol
( whitespaceopt pp-expression whitespaceopt )
При ссылке в препроцессорном выражении определенный символ условной компиляции имеет логическое значение true, а неопределенный — логическое значение false.
Вычисление препроцессорного выражения всегда дает логическое значение. Правила вычисления для препроцессорного выражения такие же, как для константного выражения (§7.19), с тем исключением, что единственные определенные пользователем сущности, на которые можно ссылаться, — это символы условной компиляции.
Директивы объявлений
Директивы объявлений используются для определения или отмены определения символов условной компиляции.
pp-declaration:
whitespaceopt # whitespaceopt define whitespace conditional-symbol pp-new-line
whitespaceopt # whitespaceopt undef whitespace conditional-symbol pp-new-line
pp-new-line:
whitespaceopt single-line-commentopt new-line
Обработка директивы #define делает данный символ условной компиляции определенным, начиная с исходной строки, следующей за директивой. Подобным же образом обработка директивы #undef делает данный символ условной компиляции неопределенным, начиная с исходной строки, следующей за директивой.
Все директивы #define и #undef в исходном файле должны стоять до первой лексемы (§2.4) в исходном файле, иначе вызывается ошибка времени компиляции. Интуитивно понятно, что директивы #define и #undef должны предшествовать любому "фактическому коду" в исходном файле.
Пример:
#define Enterprise
#if Professional || Enterprise
#define Advanced
#endif
namespace Megacorp.Data
{
#if Advanced
class PivotTable {...}
#endif
}
правильно, так как директивы#define предшествуют первой лексеме (ключевое слово namespace) в исходном файле.
Следующий пример приводит к ошибке времени компиляции, так как директива #define следует за фактическим кодом:
#define A
namespace N
{
#define B
#if B
class Class1 {}
#endif
}
Директива #define может определять уже определенный символ условной компиляции, без какого-либо вмешательства директивы #undef для этого символа. В следующем примере символ условной компиляции A определен, а затем определен снова.
#define A
#define A
Директива #undef может "отменить определение" не определенного символа условной компиляции. В следующем примере определен символ условной компиляции A, а затем дважды отменено его определение; хотя вторая директива #undef не действует, она все же допустима.
#define A
#undef A
#undef A