IT. Expert System.

C#

Pre-processing directives


The pre-processing directives provide the ability to conditionally skip sections of source files, to report error and warning conditions, and to delineate distinct regions of source code. The term “pre-processing directives” is used only for consistency with the C and C++ programming languages. In C#, there is no separate pre-processing step; pre-processing directives are processed as part of the lexical analysis phase.

pp-directive:
pp-declaration
pp-conditional
pp-line
pp-diagnostic
pp-region
pp-pragma

The following pre-processing directives are available:

  • #define and #undef, which are used to define and undefine, respectively, conditional compilation symbols .

  • #if, #elif, #else, and #endif, which are used to conditionally skip sections of source code .

  • #line, which is used to control line numbers emitted for errors and warnings .

  • #error and #warning, which are used to issue errors and warnings, respectively .

  • #region and #endregion, which are used to explicitly mark sections of source code .

  • #pragma, which is used to specify optional contextual information to the compiler .

A pre-processing directive always occupies a separate line of source code and always begins with a # character and a pre-processing directive name. White space may occur before the # character and between the # character and the directive name.

A source line containing a #define, #undef, #if, #elif, #else, #endif, or #line directive may end with a single-line comment. Delimited comments (the /* */ style of comments) are not permitted on source lines containing pre-processing directives.

Pre-processing directives are not tokens and are not part of the syntactic grammar of C#. However, pre-processing directives can be used to include or exclude sequences of tokens and can in that way affect the meaning of a C# program. For example, when compiled, the program:

#define A
#undef B

class C
{
#if A
void F() {}
#else
void G() {}
#endif

#if B
void H() {}
#else
void I() {}
#endif
}

results in the exact same sequence of tokens as the program:

class C
{
void F() {}
void I() {}
}

Thus, whereas lexically, the two programs are quite different, syntactically, they are identical.

      1. Conditional compilation symbols

The conditional compilation functionality provided by the #if, #elif, #else, and #endif directives is controlled through pre-processing expressions and conditional compilation symbols.

conditional-symbol:
Any identifier-or-keyword except true or false

A conditional compilation symbol has two possible states: defined or undefined. At the beginning of the lexical processing of a source file, a conditional compilation symbol is undefined unless it has been explicitly defined by an external mechanism (such as a command-line compiler option). When a #define directive is processed, the conditional compilation symbol named in that directive becomes defined in that source file. The symbol remains defined until an #undef directive for that same symbol is processed, or until the end of the source file is reached. An implication of this is that #define and #undef directives in one source file have no effect on other source files in the same program.

When referenced in a pre-processing expression, a defined conditional compilation symbol has the boolean value true, and an undefined conditional compilation symbol has the boolean value false. There is no requirement that conditional compilation symbols be explicitly declared before they are referenced in pre-processing expressions. Instead, undeclared symbols are simply undefined and thus have the value false.

The name space for conditional compilation symbols is distinct and separate from all other named entities in a C# program. Conditional compilation symbols can only be referenced in #define and #undef directives and in pre-processing expressions.

      1. Pre-processing expressions

Pre-processing expressions can occur in #if and #elif directives. The operators !, ==, !=, && and || are permitted in pre-processing expressions, and parentheses may be used for grouping.

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 )

When referenced in a pre-processing expression, a defined conditional compilation symbol has the boolean value true, and an undefined conditional compilation symbol has the boolean value false.

Evaluation of a pre-processing expression always yields a boolean value. The rules of evaluation for a pre-processing expression are the same as those for a constant expression , except that the only user-defined entities that can be referenced are conditional compilation symbols.

      1. Declaration directives

The declaration directives are used to define or undefine conditional compilation symbols.

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

The processing of a #define directive causes the given conditional compilation symbol to become defined, starting with the source line that follows the directive. Likewise, the processing of an #undef directive causes the given conditional compilation symbol to become undefined, starting with the source line that follows the directive.

Any #define and #undef directives in a source file must occur before the first token in the source file; otherwise a compile-time error occurs. In intuitive terms, #define and #undef directives must precede any “real code” in the source file.

The example:

#define Enterprise

#if Professional || Enterprise
#define Advanced
#endif

namespace Megacorp.Data
{
#if Advanced
class PivotTable {...}
#endif
}

is valid because the #define directives precede the first token (the namespace keyword) in the source file.

The following example results in a compile-time error because a #define follows real code:

#define A
namespace N
{
#define B
#if B
class Class1 {}
#endif
}

A #define may define a conditional compilation symbol that is already defined, without there being any intervening #undef for that symbol. The example below defines a conditional compilation symbol A and then defines it again.

#define A
#define A

A #undef may “undefine” a conditional compilation symbol that is not defined. The example below defines a conditional compilation symbol A and then undefines it twice; although the second #undef has no effect, it is still valid.

#define A
#undef A
#undef A

      1. Conditional compilation directives

The conditional compilation directives are used to conditionally include or exclude portions of a source file.

pp-conditional:
pp-if-section pp-elif-sectionsopt pp-else-sectionopt pp-endif

pp-if-section:
whitespaceopt # whitespaceopt if whitespace pp-expression pp-new-line conditional-sectionopt

pp-elif-sections:
pp-elif-section
pp-elif-sections pp-elif-section

pp-elif-section:
whitespaceopt # whitespaceopt elif whitespace pp-expression pp-new-line conditional-sectionopt

pp-else-section:
whitespaceopt # whitespaceopt else pp-new-line conditional-sectionopt

pp-endif:
whitespaceopt # whitespaceopt endif pp-new-line

conditional-section:
input-section
skipped-section

skipped-section:
skipped-section-part
skipped-section skipped-section-part

skipped-section-part:
skipped-charactersopt new-line
pp-directive

skipped-characters:
whitespaceopt not-number-sign input-charactersopt

not-number-sign:
Any input-character except #

As indicated by the syntax, conditional compilation directives must be written as sets consisting of, in order, an #if directive, zero or more #elif directives, zero or one #else directive, and an #endif directive. Between the directives are conditional sections of source code. Each section is controlled by the immediately preceding directive. A conditional section may itself contain nested conditional compilation directives provided these directives form complete sets.

A pp-conditional selects at most one of the contained conditional-sections for normal lexical processing:

  • The pp-expressions of the #if and #elif directives are evaluated in order until one yields true. If an expression yields true, the conditional-section of the corresponding directive is selected.

  • If all pp-expressions yield false, and if an #else directive is present, the conditional-section of the #else directive is selected.

  • Otherwise, no conditional-section is selected.

The selected conditional-section, if any, is processed as a normal input-section: the source code contained in the section must adhere to the lexical grammar; tokens are generated from the source code in the section; and pre-processing directives in the section have the prescribed effects.

The remaining conditional-sections, if any, are processed as skipped-sections: except for pre-processing directives, the source code in the section need not adhere to the lexical grammar; no tokens are generated from the source code in the section; and pre-processing directives in the section must be lexically correct but are not otherwise processed. Within a conditional-section that is being processed as a skipped-section, any nested conditional-sections (contained in nested #if...#endif and #region...#endregion constructs) are also processed as skipped-sections.

The following example illustrates how conditional compilation directives can nest:

#define Debug // Debugging on
#undef Trace // Tracing off

class PurchaseTransaction
{
void Commit() {
#if Debug
CheckConsistency();
#if Trace
WriteToLog(this.ToString());
#endif
#endif
CommitHelper();
}
}

Except for pre-processing directives, skipped source code is not subject to lexical analysis. For example, the following is valid despite the unterminated comment in the #else section:

#define Debug // Debugging on

class PurchaseTransaction
{
void Commit() {
#if Debug
CheckConsistency();
#else
/* Do something else
#endif
}
}

Note, however, that pre-processing directives are required to be lexically correct even in skipped sections of source code.

Pre-processing directives are not processed when they appear inside multi-line input elements. For example, the program:

class Hello
{
static void Main() {
System.Console.WriteLine(@"hello,
#if Debug
world
#else
Nebraska
#endif
");
}
}

results in the output:

hello,
#if Debug
world
#else
Nebraska
#endif

In peculiar cases, the set of pre-processing directives that is processed might depend on the evaluation of the pp-expression. The example:

#if X
/*
#else
/* */ class Q { }
#endif

always produces the same token stream (class Q { }), regardless of whether or not X is defined. If X is defined, the only processed directives are #if and #endif, due to the multi-line comment. If X is undefined, then three directives (#if, #else, #endif) are part of the directive set.

      1. Diagnostic directives

The diagnostic directives are used to explicitly generate error and warning messages that are reported in the same way as other compile-time errors and warnings.

pp-diagnostic:
whitespaceopt # whitespaceopt error pp-message
whitespaceopt # whitespaceopt warning pp-message

pp-message:
new-line
whitespace input-charactersopt new-line

The example:

#warning Code review needed before check-in

#if Debug && Retail
#error A build can't be both debug and retail
#endif

class Test {...}

always produces a warning (“Code review needed before check-in”), and produces a compile-time error (“A build can’t be both debug and retail”) if the conditional symbols Debug and Retail are both defined. Note that a pp-message can contain arbitrary text; specifically, it need not contain well-formed tokens, as shown by the single quote in the word can’t.

      1. Region directives

The region directives are used to explicitly mark regions of source code.

pp-region:
pp-start-region conditional-sectionopt pp-end-region

pp-start-region:
whitespaceopt # whitespaceopt region pp-message

pp-end-region:
whitespaceopt # whitespaceopt endregion pp-message

No semantic meaning is attached to a region; regions are intended for use by the programmer or by automated tools to mark a section of source code. The message specified in a #region or #endregion directive likewise has no semantic meaning; it merely serves to identify the region. Matching #region and #endregion directives may have different pp-messages.

The lexical processing of a region:

#region
...
#endregion

corresponds exactly to the lexical processing of a conditional compilation directive of the form:

#if true
...
#endif

      1. Line directives

Line directives may be used to alter the line numbers and source file names that are reported by the compiler in output such as warnings and errors.

Line directives are most commonly used in meta-programming tools that generate C# source code from some other text input.

pp-line:
whitespaceopt # whitespaceopt line whitespace line-indicator pp-new-line

line-indicator:
decimal-digits whitespace file-name
decimal-digits
default
hidden

file-name:
" file-name-characters "

file-name-characters:
file-name-character
file-name-characters file-name-character

file-name-character:
Any input-character except "

When no #line directives are present, the compiler reports true line numbers and source file names in its output. When processing a #line directive that includes a line-indicator that is not default, the compiler treats the line after the directive as having the given line number (and file name, if specified).

A #line default directive reverses the effect of all preceding #line directives. The compiler reports true line information for subsequent lines, precisely as if no #line directives had been processed.

A #line hidden directive has no effect on the file and line numbers reported in error messages, but does affect source level debugging. When debugging, all lines between a #line hidden directive and the subsequent #line directive (that is not #line hidden) have no line number information. When stepping through code in the debugger, these lines will be skipped entirely.

Note that a file-name differs from a regular string literal in that escape characters are not processed; the ‘\’ character simply designates an ordinary backslash character within a file-name.

      1. Pragma directives

The #pragma preprocessing directive is used to specify optional contextual information to the compiler. The information supplied in a #pragma directive will never change program semantics.

pp-pragma:
whitespaceopt # whitespaceopt pragma whitespace pragma-body pp-new-line

pragma-body:
pragma-warning-body

C# provides #pragma directives to control compiler warnings. Future versions of the language may include additional #pragma directives. To ensure interoperability with other C# compilers, the Microsoft C# compiler does not issue compilation errors for unknown #pragma directives; such directives do however generate warnings.

        1. Pragma warning

The #pragma warning directive is used to disable or restore all or a particular set of warning messages during compilation of the subsequent program text.

pragma-warning-body:
warning whitespace warning-action
warning whitespace warning-action whitespace warning-list

warning-action:
disable
restore

warning-list:
decimal-digits
warning-list whitespaceopt , whitespaceopt decimal-digits

A #pragma warning directive that omits the warning list affects all warnings. A #pragma warning directive the includes a warning list affects only those warnings that are specified in the list.

A #pragma warning disable directive disables all or the given set of warnings.

A #pragma warning restore directive restores all or the given set of warnings to the state that was in effect at the beginning of the compilation unit. Note that if a particular warning was disabled externally, a #pragma warning restore (whether for all or the specific warning) will not re-enable that warning.

The following example shows use of #pragma warning to temporarily disable the warning reported when obsoleted members are referenced, using the warning number from the Microsoft C# compiler.

using System;

class Program
{
[Obsolete]
static void Foo() {}

static void Main() {
#pragma warning disable 612
Foo();
#pragma warning restore 612
}
}

  1. Basic concepts

    1. Application Startup

      An assembly that has an entry point is called an application. When an application is run, a new application domain is created. Several different instantiations of an application may exist on the same machine at the same time, and each has its own application domain.

      An application domain enables application isolation by acting as a container for application state. An application domain acts as a container and boundary for the types defined in the application and the class libraries it uses. Types loaded into one application domain are distinct from the same type loaded into another application domain, and instances of objects are not directly shared between application domains. For instance, each application domain has its own copy of static variables for these types, and a static constructor for a type is run at most once per application domain. Implementations are free to provide implementation-specific policy or mechanisms for the creation and destruction of application domains.

      Application startup occurs when the execution environment calls a designated method, which is referred to as the application's entry point. This entry point method is always named Main, and can have one of the following signatures:

      static void Main() {...}

      static void Main(string[] args) {...}

      static int Main() {...}

      static int Main(string[] args) {...}

      As shown, the entry point may optionally return an int value. This return value is used in application termination .

      The entry point may optionally have one formal parameter. The parameter may have any name, but the type of the parameter must be string[]. If the formal parameter is present, the execution environment creates and passes a string[] argument containing the command-line arguments that were specified when the application was started. The string[] argument is never null, but it may have a length of zero if no command-line arguments were specified.

      Since C# supports method overloading, a class or struct may contain multiple definitions of some method, provided each has a different signature. However, within a single program, no class or struct may contain more than one method called Main whose definition qualifies it to be used as an application entry point. Other overloaded versions of Main are permitted, however, provided they have more than one parameter, or their only parameter is other than type string[].

      An application can be made up of multiple classes or structs. It is possible for more than one of these classes or structs to contain a method called Main whose definition qualifies it to be used as an application entry point. In such cases, an external mechanism (such as a command-line compiler option) must be used to select one of these Main methods as the entry point.

      In C#, every method must be defined as a member of a class or struct. Ordinarily, the declared accessibility of a method is determined by the access modifiers specified in its declaration, and similarly the declared accessibility of a type is determined by the access modifiers specified in its declaration. In order for a given method of a given type to be callable, both the type and the member must be accessible. However, the application entry point is a special case. Specifically, the execution environment can access the application's entry point regardless of its declared accessibility and regardless of the declared accessibility of its enclosing type declarations.

      The application entry point method may not be in a generic class declaration.

      In all other respects, entry point methods behave like those that are not entry points.



Content

Android Reference

Java basics

Java Enterprise Edition (EE)

Java Standard Edition (SE)

SQL

HTML

PHP

CSS

Java Script

MYSQL

JQUERY

VBS

REGEX

C

C++

C#

Design patterns

RFC (standard status)

RFC (proposed standard status)

RFC (draft standard status)

RFC (informational status)

RFC (experimental status)

RFC (best current practice status)

RFC (historic status)

RFC (unknown status)

IT dictionary

License.
All information of this service is derived from the free sources and is provided solely in the form of quotations. This service provides information and interfaces solely for the familiarization (not ownership) and under the "as is" condition.
Copyright 2016 © ELTASK.COM. All rights reserved.
Site is optimized for mobile devices.
Downloads: 238 / 158767610. Delta: 0.03130 с