C# Core Language Features

Now that some basic examples and the framework of Visual C# 2008 code have been presented, we can discuss the fundamental building blocks of any C# application. This section starts by discussing symbols and tokens, the most elemental components of a Visual C# 2008 application.

Symbols and Tokens

Symbols and tokens are the basic constituents of the C# language. C# statements consist of symbols and tokens—indeed, they cannot be assembled without them. Table 1-2 provides a list of the C# symbols and tokens. Each entry in Table 1-2 is explained in the text that follows.

Table 1-2. C# Symbols and tokens

Description

Symbols or tokens

White space

Space, Form Feed

Tab

Horizontal_tab, Vertical_tab

Punctuator

. , : ;

Line terminator

Carriage return, line feed, next line character, line separator, paragraph separator, carriage return and line feed together

Comment

// /* */ /// /** */

Preprocessor directive

#

Block

{}

Lambda expression

=>

Generics

< >

Nullable type

?

Character

Unicode_character

Escape character

code

Integer suffix (case-insensitive)

u l ul lu

Real suffix (case-insensitive)

f d m

Operator

+ - * % / > < ? ?? ( ) [ ] | || ^ ! ~ ++ -- = is as & && -> :: << >>

Compound operator

== != <= >= += -= *= /= %= &= |= ^= <<= >>= =>

White Space

White space is defined as a space, horizontal tab, vertical tab, or form feed character. White space characters can be combined; where one whitespace character is required, two or more contiguous characters of white space can be substituted.

Tabs

Tabs—horizontal and vertical—are white-space characters, as discussed just previously.

Punctuators

Punctuators separate and delimit elements of the C# language. Punctuators include the semicolon (;), dot (.), colon (:), and comma (,).

Semicolon punctuator

In Visual C#, statements are terminated with a semicolon (;). C# is a free-form language in which a statement can span multiple lines of source code and can start in any position. Conversely, multiple statements can be combined on a single source code line. Here are some variations:

int variablea =
          variableb +
                variablec;

variableb = variableb + 3; variablec = variablec + 1;

++variableb;

Dot punctuator

Dot syntax connotes membership. The dot character (.) binds a target to a member, in which the target can be a namespace, type, structure, enumeration, interface, or object. This assumes the member is accessible. Membership is sometimes nested and described with additional dots.

Here is the syntax for the dot punctuator:

Target.Member

This is an example of the dot punctuator:

System.Windows.Forms.MessageBox.Show("A nice day!");

System, Windows, and Forms are namespaces. MessageBox is a class. Show, the most nested member, is a static method.

Colon punctuator

The colon punctuator primarily delimits a label, indicates inheritance, indicates interface implementation, sets a generic constraint, or is part of a conditional operator.

Labels are tags for locations to which program execution can be transferred. A label is terminated with a colon punctuator (:). The scope of a label is limited to the containing block and any nested block. There are various methods for transferring to a label. For example, you can jump to a label with the goto statement. Within a switch block, you also can use the goto statement to jump to a case or default statement.

Here is the syntax for the label punctuator:

label_identifier: statement

A statement must follow a label, even if it’s an empty statement.

Here is an example of a goto statement:

   public static void Main() {
       goto one;
       // do stuff
one:   Console.WriteLine("one");
   }

Comma punctuator

The comma punctuator delimits array indexes, function parameters, types in an inheritance list, statement clauses, and other language elements. The comma punctuator separates clauses of a for statement in the following code:

for (int iBottom = 1, iTop = 10; iBottom < iTop; ++iBottom, --iTop) {
    Console.WriteLine("{0}x{1} {2}", iBottom, iTop, iBottom*iTop);
}

A statement clause is a substatement in which multiple statement clauses can be combined into a single statement. Statement clauses are not always available—check documentation related to the language artifact to be sure.

Line Terminators

Line terminators separate lines of source code. Where one line terminator is available, two or more are allowed. Except in string literals, line terminators can be inserted anywhere white space is allowed. The following code is syntactically incorrect:

int variableb, variablec;

int variablea = var
            iableb+variablec; // wrong!

The variableb identifier cannot contain spaces. Therefore, it also cannot contain a line terminator.

Comments

C# supports four styles of comments: single-line, delimited, single-line documentation, and multi-line documentation comments. Although comments are not required, the liberal use of comments is considered good programming style. Be kind to those maintaining your program (present and future) —comment! I highly recommend reading Code Complete, Second Edition (Microsoft Press, 2004), by Steve McConnell; this book provides valuable best practices on programming, including how to document source code properly.

Single-line comments: //

Single-line comments start at the comment symbol and conclude at the line terminator, as follows:

Console.WriteLine(objGreeting.French); // Display Hello (French)

Delimited comments: /* and */

Delimited comments, also called multi-line or block comments, are bracketed by the /* and */ symbols. Delimited comments can span multiple lines of source code:

/*
        Class Program: Programmer Donis Marshall
*/
class Program {
    static int Main(string[] args) {
        Greeting objGreeting = new Greeting();
        Console.WriteLine(objGreeting.French); // Display Hello (French)
        return 0;
    }
}

Single-line documentation comments: ///

Documentation comments apply a consistent format to source code comments and use XML tags to classify comments. With the documentation generator, documentation comments are exportable to an XML file. The resulting file is called the documentation file, which is identified in the Visual Studio project options. IntelliSense and the Object Browser use information in this file.

Single-line documentation comments are partially automated in the Visual Studio IDE. The Visual Studio IDE has Smart Comment Editing, which automatically continues or creates a skeleton for a document comment after initially entering the /// symbol. For example, the following code snippet shows sample code with single-line documentation comments. After entering an initial ///, Smart Comment Editing completed the remainder of the comment framework, including adding comments and XML tags for the type, methods, method parameter, and return value. You only need to update the comment framework with specific comments and additional comment tags that might be helpful:

/// <summary>
///
/// </summary>
class Program {
    /// <summary>
    ///
    /// </summary>
    /// <param name="args"></param>
    /// <returns></returns>
    static int Main(string[] args) {
         Greeting objGreeting = new Greeting();
         Console.WriteLine(objGreeting.French); // Display Hello (French)
         return 0;
    }
}

Here are the documentation comments with added details:

/// <summary>
/// Starter class for Simple HelloWorld
/// </summary>
class Program {
    /// <summary>
    /// Program Entry Point
    /// </summary>
    /// <param name="args">Command Line Parameters</param>
    /// <returns>zero</returns>
    static int Main(string[] args) {
          Greeting objGreeting = new Greeting();
          Console.WriteLine(objGreeting.French);   // Display Hello (French)
          return 0;
    }
}

The C# compiler is a documentation generator. The /doc compiler option instructs the compiler to generate the documentation file. This can be done using the Visual Studio IDE. Select Project Properties from the Project menu. In the Properties window, select the Build tab. Toward the bottom of the Build pane (shown in Figure 1-2), you can specify the name of the XML documentation file.

The Build pane of the Project Settings window

Figure 1-2. The Build pane of the Project Settings window

For the preceding source file, this is the documentation file generated by the C# compiler:

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>ConsoleApplication1</name>
    </assembly>
    <members>
        <member name="T:ConsoleApplication1.Program">
            <summary>
            Starter class for Simple HelloWorld
            </summary>
        </member>
        <member name="M:ConsoleApplication1.Program.Main(System.String[])">
            <summary>
            Program Entry Point
            </summary>
            <param name="args">Command Line Parameters</param>
            <returns>zero</returns>
        </member>
    </members>
</doc>

The documentation generator prefixes IDs to element names of the member name tag. In the preceding documentation file, T is the prefix for a type, whereas M is a prefix for a method. Here’s a listing of IDs:

E

Event

F

Field

M

Method

N

Namespace

P

Property

T

Type

!

Error

Multi-line documentation tags

Multi-line documentation tags are an alternative to single-line documentation tags. Smart Comment Editing is not available with multi-line documentation tags. You must enter the documentation tags explicitly. However, Intellisense is available. Multi-line documentation comments must adhere to a degree of consistency, which is explained in the article titled "Delimiters for Documentation Tags (C# Programming Guide)," in Visual Studio Help (ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_csref/html/9b2bdd18-4f5c-4c0b-988e-fb992e0d233e.htm).

Here is an example of delimited documentation tags:

/**
 *<summary>this is an example.</summary>
*/

Preprocessor Directives

Preprocessor directives define symbols, undefine symbols, include source code, exclude source code, name sections of source code, and set warning and error conditions. The variety of preprocessor directives is limited compared with C++, and many of the C++ preprocessor directives are not available in C#. There is not a separate preprocessor or compilation stage for preprocessor statements. Preprocessor statements are processed by the normal C# compiler. The term preprocessor is used because the preprocessor directives are semantically similar to related commands in C++.

Here is the syntax for a preprocessor directive:

#command expression

This is a list of preprocessor directives available in C#:

  • #define

  • #else

  • #line

  • #region

  • #undef

  • #elif

  • #error

  • #endregion

  • #if

  • #endif

  • #warning

  • #pragma

The preprocessor symbol (#) and subsequent directive are optionally separated with white space but must be on the same line. A preprocessor directive can be followed with a single-line comment but not a multi-line comment.

Declarative preprocessor directives

The declarative preprocessor directives are #define and #undef, which define and undefine a preprocessor symbol, respectively. Defined symbols are implicitly true, whereas undefined symbols are false. Declarative symbols must be defined in each compilation unit where the symbol is referenced. Undeclared symbols default to undefined and false. The #define and #undef directives must precede any source code. Redundant #define and #undef directives have no effect.

Preprocessor symbols can also be set as a compiler option. In the Build pane of the Project Properties dialog box, you can define one or more symbols in the Conditional Compilation Symbols text box. This is shown in Figure 1-3. Other preprocessor symbols, such as DEBUG and TRACE, are commonly set implicitly as a compiler option.

The Conditional Compilation Symbols text box

Figure 1-3. The Conditional Compilation Symbols text box

Here is the syntax for declarative preprocessor directives:

#define identifier
#undef identifier

Conditional preprocessor directives

Conditional preprocessor directives are the #if, #else, #elif, and #endif directives, which exclude or include source code. A conditional preprocessor directive begins with #if and ends with #endif. The intervening conditional preprocessing directives, #else and #elif, are optional.

Here is the syntax for conditional preprocessor directives:

#if Boolean_expression
#elif Boolean_expression
#else
#endif

The Boolean_expression of the #if and #elif directive is a combination of preprocessor symbols and Boolean operators (! == != && ||). If the Boolean_expression is true, the source code immediately after the #if or #elif directive and before the next conditional preprocessor directive is included in the compilation. If the Boolean_expression is false, the source code is excluded from source compilation. The #else directive can be added to a #if or #elif combination. If the Boolean_expression of #if and #elif is false, the code following the #else is included in the compilation. When true, the source code after the #else is not included. Here’s sample code with preprocessor symbols and related directives:

#define DEBUGGING

using System;

namespace Donis.CSharpBook {
    class Starter{
#if DEBUGGING
        static void OutputLocals() {
              Console.WriteLine("debugging...");
        }
#endif
        static void Main() {
#if DEBUGGING
            OutputLocals();
#endif
        }
    }
}

Finally, the #elif directive is a combination of an else and if conditional preprocessor directive. It is matched with the nearest #if directive:

#if expression
    source_code
#elif expression
    source_code
#else
    source_code
#endif

Diagnostic directives

Diagnostic directives include the #error, #warning, and #pragma directives. The #error and #warning directives display error and warning messages, respectively. The diagnostic messages are displayed in the Error List window of the Visual Studio IDE. Similar to standard compilation errors, an #error directive prevents the program from compiling successfully; a #warning directive does not prevent the program from successfully compiling unless Treat Warnings As Error is set as a compiler option. You can use conditional directives to conditionally apply diagnostic directives.

Here is the syntax for diagnostic directives:

#error error_message
#warning error_message

The error_message is of string type and is optional.

Pragma directives

The #pragma directive disables or enables compilation warnings. When disabled, the warning or error is suppressed and will not appear in the Error List window. This is useful for suppressing an unwanted warning or error temporarily or permanently.

Here is the syntax for pragma directives:

#pragma warning disable warning_list
#pragma warning restore warning_list

The warning_list contains one or more warnings delimited with commas. A disabled warning remains disabled until it is restored or the compilation unit ends.

The following code demonstrates the pragma warning directive. In this example, the 219 warning (Variable Is Assigned But Its Value Is Never Used) is initially disabled and then restored. Therefore a warning is received about variableb but not variablea.

class Starter
{
#pragma warning disable 219
    static void Main()
    {
        int variablea = 10;
    }
#pragma warning restore 219

    static void FuncA()
    {
        int variableb = 20;
    }
}

Region directives

Region directives mark sections of source code. The #region directive starts a region, whereas the #endregion directive ends the region. Region directives can be nested. The Visual Studio IDE outlines the source code based on region directives. In Visual Studio, you can collapse or expand regions of source code.

Here is the syntax for region directives:

#region identifier
source_code
#endregion

Line directives

Line directives modify the line number reported in subsequent compiler errors and warnings. There are three versions of the line directive.

Here is the syntax for line directives:

#line line_number source_filename
#line default
#line hidden

The first #line directive renumbers the source code from the location of the directive until the end of the compilation unit is reached or overridden by another #line directive. In the following code, the #line directive resets the current reporting line to 25:

#line 25
static void Main() {
    Console.WriteLine("#line application");
    int variablea=10;  // 219 warning
}

The #line default directive undoes any previous #line directives. The line number is then reset to the natural line number.

The #line hidden directive is only tangentially related to the line number. This directive does not affect the line number; it hides source code from the debugger when stepping. The source code is skipped until the next #line directive is encountered. This is helpful in stepping through source code. Hidden code is essentially stepped over. For example, when stepping in the following source, the for loop is stepped over. The #line default directive then returns normal stepping.

    static void Main()
    {
        int variablea = 10;
        variablea++;
#line hidden
        for (int i = 0; i < 5; ++i)
        {
            variablea++;
        }
#line default
        Console.WriteLine(variablea);
    }

Blocks

A type, which can be a class, struct, interface, or enum, is defined within a block. Members of the type are contained inside the block.

Here is the syntax for a type block:

type typename { // block
}

A block can also be a statement block. Statement blocks contain one or more statements. Each statement of the statement block is delimited by a semicolon. Typically, where a single statement is allowed, a statement block can be substituted. Statement blocks are commonly used as function bodies, conditional statements, and iterative statements. For function bodies, a statement block is required.

The if path in the following code consists of a single statement. Therefore, a statement block is not required. The Console.WriteLine is the only statement within the context of the if statement:

static void Main() {
    int variablea = 5, variableb = 10;
    if (((variablea + variableb) % 2) == 0)
        Console.WriteLine("the sum is even");
}

In the modified code, the if path contains multiple statements and a statement block is needed. Some would suggest, and I agree, that always using statement blocks with conditional or iterative statements is a good practice. This prevents an inadvertent future error when a single statement is expanded to multiple statements as shown below, but the block is forgotten:

static void Main() {
    int variablea = 5, variableb = 10;
    if (((variablea + variableb) % 2) == 0) {
        Console.WriteLine("{0} {1}", variablea,
            variableb);
        Console.WriteLine("the sum is even");
    }
}

Generic types

A generic is an abstraction of a type, which itself is an abstraction of a noun, place, or thing.

The NodeInt class is an abstraction of a node within a linked list of integers. The following is a partial implementation of the code (the full implementation is presented later in this book):

class NodeInt {
    public NodeInt(int f_Value, NodeInt f_Previous) {
        m_Value = f_Value;
        m_Previous = f_Previous;
    }

    // Remaining methods

    private int m_Value;
    private NodeInt m_Previous;
}

The Node generic type further abstracts a linked list. Unlike NodeInt, Node is not integer-specific but a linked list of any type. In the generic type, integer specifics of the NodeInt class have been removed and substituted with placeholders.

class Node<T> {
    public Node(T f_Value, Node<T> f_Previous) {
        m_Value = f_Value;
        m_Previous = f_Previous;
    }
        // Remaining methods

        private T m_Value;
        private Node<T> m_Previous;
}

In the preceding example, T is the generic type parameter, which is then used as a placeholder throughout the class for future type substitution.

There is much more about generics later in Chapter 7.

Characters

C# source files contain Unicode characters, which are the most innate of symbols. Every element, keyword, operator, or identifier in the source file is a composite of Unicode characters.

Numeric Suffixes

Numeric suffixes cast a literal value to the underlying or a related type. Literal integer values can have the l, u, ul, and lu suffixes appended to them; literal real values can have the f, d, and m suffixes added. The suffixes are case-insensitive. Table 1-3 describes each suffix.

Table 1-3. Description of suffixes

Description

Type

Suffix

Unsigned integer or unsigned long

uint or ulong

U

Long or unsigned long

long or ulong

L

Unsigned long

ulong

ul or lu

Float

float

F

Double

double

D

Money

decimal

M

When casting a real type using the m suffix (for monetary or currency calculations), rounding might be required. If so, banker’s rounding is used.

Here is an example of a numeric suffix:

uint variable = 10u;

Escape Characters

The escape character provides an alternate means of encoding Unicode characters, which is particularly useful for special characters that are not available on a standard keyboard. Escape sequences can be embedded in identifiers and string literals. Unicode escape sequences must have four hexadecimal digits and are limited to a single character.

A Unicode escape sequence looks like this:

u hexdigit1 hexdigit2 hexdigit3 hexdigit4

Hexadecimal escape sequences contain one or more digits as defined by the location of a Unicode character.

A hexadecimal escape sequence looks like this:

x hexdigit1 hexdigit2 ... hexdigitn

Table 1-4 shows a list of the predefined escape sequences in C#.

Table 1-4. Predefined escape sequences

Simple escape

Sequence

Single quote

Double quote

"

Backslash

\

Null

Alert

a

Backspace



Form feed

f

New line

Carriage return

Horizontal tab

Unicode character

u

Vertical tab

v

Hexadecimal character(s)

x

This is an unconventional version of the traditional "Hello World!" program:

using System;

class HelloWorld {
    static void Main() {
        Console.Write("u0048u0065u006Cu006Cu006F
");
        Console.Write("x77x6Fx72x6Cx64x21");
    }
}

Verbatim Characters

The verbatim character (@) prevents the translation of a string or identifier, where it is treated "as-is." To create a verbatim string or identifier, prefix it with the verbatim character. This is helpful, for example, when storing directory paths in a string literal.

A verbatim string is a string literal prefixed with the verbatim character. The characters of the verbatim string, including escape sequences, are not translated. The exception is the escape character for quotes, which is translated even in a verbatim string. Unlike a normal string, verbatim strings can even contain physical line feeds.

Here is a sample verbatim string:

using System;

class Verbatim{
    static void Main() {
        string fileLocation = @"c:datafile.txt";
        Console.WriteLine("File is located at {0}",
                           fileLocation);
    }
}

A verbatim identifier is an identifier prefixed with the verbatim character that prevents the identifier from being parsed as a keyword. When porting source code from another programming language where allowable keywords and identifiers may be different, this feature could be useful. Otherwise, it is a best practice not to use this technique because verbatim identifiers almost always make your code less readable and harder to maintain.

The following source code is technically correct:

public class ExampleClass {
    public static void Function() {
        int @for = 12;
        MessageBox.Show(@for.ToString());
    }
}

In the preceding code, the for keyword is being used as a variable name. This is confusing at best. The for keyword is common in C# and many other programming languages, and therefore most developers would find this code confusing.

Operators

Operators are used in expressions and always return a value. There are three categories of operators: unary, binary, and ternary. Some operators, such as is, as, and default, also are considered keywords. The following sections describe all the operators in C#.

Unary operators

Unary operators have a single operand. Table 1-5 lists the unary operators.

Table 1-5. Unary operators

Operator

Symbol

Sample

Result

unary plus

+

variable = +5;

5

unary minus

variable = (10);

10

Boolean negation

!

variable = !true;

false

bitwise 1’s complement

~

variable = ~((uint)1);

4294967294

prefix increment

++

++variable;

11

prefix decrement

– –

– –variable;

10

postfix increment

++

variable++;

11

postfix decrement

– –

variable – –;

10

cast

( )

variable = (int)123.45;

123

function

( )

FunctionCall(parameter);

return value

array index

[]

arrayname[index];

nth element

dot

.

container.member

member

global namespace qualifier

::

global::globalmember

Globalmember

Here are some more details on unary parameters:

  • Prefix operators are evaluated before the encompassing expression.

  • Postfix operators are evaluated after the encompassing expression.

Binary operators

Binary operators have a left and right operand. Table 1-6 details the binary operators.

Table 1-6. Binary operators

Operator

Symbol

Sample

Result

assignment

=

variable =10;

10

binary plus

+

variable = variable + 5;

15

binary minus

variable = variable10;

5

multiplication

*

variable = variable * 5;

25

division

/

variable = variable / 5;

5

modulus

%

variable = variable % 3;

2

bitwise AND

&

variable = 5 & 3;

1

bitwise OR

|

variable = 5 | 3;

7

bitwise XOR

^

variable = 5 ^ 3;

6

bitwise shift left

<<

variable = 5 << 3;

40

bitwise shift right

>>

variable = 5 >> 1;

2

null coalescing

??

variableb = variable??5

2

Here’s more information on binary operators:

  • Integer division truncates the floating point portion of the result.

  • The operands in a bitwise shift left are value << bitcount.

  • The operands in a bitwise shift right are value >> bitcount.

Compound operators

Compound operators combine an assignment and another operator. If the expanded expression is variable = variable operator value, the compound operator is variable operator= value. For example, assume that we want to code the following:

variable = variable + 5;

The preceding statement is equivalent to this:

variable += 5;

Compound operations are a shortcut and are never required in lieu of the expanded statement. Table 1-7 lists the compound operators.

Table 1-7. Compound operators

Operator

Symbol

Sample

addition assignment

+=

variable += 5;

subtraction assignment

=

variable= 10;

multiplication assignment

*=

variable *= 5;

division assignment

/=

variable /= 5;

modulus assignment

%=

variable %= 3;

AND assignment

&=

variable &= 3;

OR assignment

|=

variable |= 3;

XOR assignment

^=

variable ^= 3;

left-shift assignment

<<=

variable <<= 3;

right-shift assignment

>>=

variable >>= 1;

Boolean operators

Boolean expressions evaluate to true or false. Unlike C++, the integer values of nonzero and zero are not equivalent to a Boolean true or false.

There are two versions of the logical and and or operators. The && and || operators support short-circuiting, whereas & and | do not. What is short-circuiting? If the result of the expression can be determined with the left side, the right side is not evaluated. Without disciplined coding practices, short-circuiting might cause unexpected side effects.

Next is an example of possible short-circuiting. If FunctionA evaluates to false, the entire Boolean expression is false. Therefore the right-hand expression (FunctionB) is not evaluated. If calling FunctionB has a required side effect, short-circuiting in this circumstance could cause a bug.

if (FunctionA() && FunctionB()) {

}

Table 1-8 shows the Boolean operators.

Table 1-8. Boolean operators

Operator

Symbol

equals

==

not equal

!=

less than

<

greater than

>

logical AND (allows short-circuiting)

&&

logical OR (allows short-circuiting)

||

logical AND

&

logical OR

|

less than or equal

<=

greater than or equal

>=

logical XOR

^

Ternary operators

The conditional operator is the sole ternary operator in C# and is an abbreviated if else statement.

Here is the syntax of the conditional operator:

Boolean_expression ? true_statement : false_statement

This is the conditional operator in source code:

char e = (x > 0) ? '>' : '<'

Type operators

Type operators act on a type. The as and is operators are binary operators, while the typeof operator is unary. Table 1-9 lists the type operators.

Table 1-9. Type operators

Operator

Syntax

Description

as

object as type

Casts object to type if possible. If not, returns null.

is

object is type

Expression evaluates to true if object is related to type; otherwise, evaluates to false.

typeof

typeof(object)

Returns the type of the object.

Pointer operators

Pointer operators are available in unsafe mode. This allows C# developers to use C++ style pointers. The unsafe compiler option sets unsafe mode. From the Visual Studio IDE, you can choose the Allow Unsafe Mode option in the Project Properties dialog box on the Build pane. Table 1-10 lists the pointer operators.

Table 1-10. Pointer operators

Operator

Symbol

Description

asterisk operator (postfix)

*

Declare a pointer

asterisk operator (prefix)

*

Dereference a pointer

ampersand operator

&

Obtain an address

arrow operator

->

Dereference a pointer and member access

Here is some sample code using pointers:

static void Main(string[] args)
{
   unsafe {
         int variable = 10;
         int* pVariable = &variable;
         Console.WriteLine("Value at address is {0}.",
               *pVariable);
   }
}

A more extensive review of pointers is presented later in the book in Chapter 19, "Unsafe Code."

Miscellaneous operators

The miscellaneous operators are unary but do not otherwise fit into a clear category. Table 1-11 lists the miscellaneous operators.

Table 1-11. Miscellaneous operators

Operator

Syntax

Description

New

new type(parameters)

Calls a matching constructor of the type. For a reference type, creates an instance of the object on the managed heap. For a value type, creates an instance of an initialized object on the stack.

checked

checked(expression)

Exception is raised if expression overflows.

delegate

delegate return_type method

Defines a type that holds type-safe function references.

lambda

=>

Separates the input and expression body of a lambda expression.

unchecked

unchecked(expression)

Overflows are ignored in expression.

Identifiers

An identifier is the name of a C# entity, which includes type, method, property, field, and other names. Identifiers can contain Unicode characters, escape character sequences, and underscores. A verbatim identifier is prefixed with the verbatim character (as discussed in the section "Verbatim Characters" earlier in this chapter).

Keywords

One of the strengths of C# is that the language has relatively few keywords. Table 1-12 provides an overview of the C# keywords. Extended explanations of each keyword are provided in context at the appropriate location in this book.

Table 1-12. Overview of C# keywords with explanations

Keyword

Syntax

Explanation

abstract

abstract class identifier

The class cannot be instantiated.

 

abstract method

The method is implemented in a descendant class. This includes properties, indexers, and events.

base

base.member

Accesses a member of the base class.

break

break

Exits current loop or switch statement.

case

case label

Target of a switch expression.

catch

catch(filter){ handler }

The catch clause is where an exception is handled. The exception filter determines if the exception is handled in the handler.

checked

checked { statement }

If an expression within the statement_block overflows, throws an exception.

class

class identifier

Defines a new class.

const

const type identifier

Declares a constant local variable or field. Constants cannot be modified.

continue

continue

Continues to the next iteration of the loop, if any.

do

do { statement } while (expression)

The do loop is iterated until the expression is false.

else

else { statement }

The else statement is matched to the nearest if statement and provides the false path.

enum

enum identifier

Defines an enumeration type.

event

event delegatename identifier

Defines an event of the delegatename type.

explicit

explicit operator conversiontype

This user-defined conversion requires an explicit cast.

extern

extern return_type method

A method implemented externally—outside the current assembly.

false

false

A Boolean value.

finally

finally { statement }

Associated with the nearest try block. The finally block has cleanup code for resources defined in the try block.

fixed

fixed (declaration)

Fixes a pointer variable in memory and prevents relocation of that variable by the Garbage Collector.

for

for (initializers; Boolean_expression; iterators) statement

The for loop iterates the statement until the Boolean_expression is false.

foreach

foreach (element in enumerable_collection)

Iterates elements in a enumerable_collection.

get

get

Accessor method of a property member.

goto

goto identifier

Transfers control to a label.

 

goto case identifier

Transfers control to a label inside a switch statement.

 

goto default

Transfers control to a default label inside a switch statement.

if

if (Boolean_expression) statement

The statement is executed if the Boolean_expression resolves to true.

implicit

implicit operator conversiontype

This user-defined conversion requires only an implicit cast.

in

foreach (element in enumerable_collection)

Iterate elements in an enumerable_collection.

interface

interface identifier

Defines an interface.

internal

internal identifier

Type or member accessible only within the current assembly.

lock

lock(object) { statement }

Statement blocks locked on the same object are protected by a shared critical section, and access to those blocks is synchronized.

namespace

namespace identifier

Defines a namespace.

new

new return_type method

The new method hides the matching method of the base class.

 

new type

The new operator declares a new structure or class. The new operator is not required for structures.

null

null

null can be assigned to references and nullable value types.

object

object

The object keyword is an alias for System.Object, which is the base class to all .NET objects (value or reference type).

operator

operator operator

Define a user-defined operator as a class or struct member.

out

out type parameter

The actual parameter is passed by reference into the method and can be modified directly. The parameter can be uninitialized prior to the function call.

override

override method

Override a virtual method in a base class. Method includes a member function, property, indexer, or an event.

params

params type [] identifier

Variable-length parameter list. The params parameter must be the last parameter in a parameter list.

private

private member

The member is visible only within the containing class or struct.

protected

protected member

Protected members are visible to the parent and any descendant classes.

public

public member

Public members are visible to everyone. This includes inside and outside the class.

readonly

readonly type identifier

Read-only fields can be initialized at declaration or in a constructor but nowhere else.

ref

ref type parameter

The parameter is passed by reference into the method and can be modified directly in the called function. The parameter must be initialized prior to the function call. The ref keyword is also required at the call site.

return

return expression

Returns the result of an expression from a method. Functions with a void return can have a return statement without a value (i.e., return;).

sealed

sealed identifier

Class is not inheritable.

set

set

Mutator method of property member.

sizeof

sizeof(valuename)

Sizeof returns the size of a value type. Sizeof of non-primitive types requires unsafe mode.

stackalloc

stackalloc type [expression]

Allocates an array of a value type on the stack; available only in unsafe mode. Expression determines the size of the array.

static

static method

Method is class-bound and not associated with a specific object instance.

struct

struct identifier

Defines a new structure.

switch

switch(expression) { statement }

Control is transferred to either a matching case label or the default label, if present.

this

this

The this object is a reference to the current object instance.

throw

throw object

Throws a user-defined exception. Exception objects should be derived from System.Exception.

true

true

A Boolean value.

try

try { statement }

Code in statement_block of try is guarded. If an exception is raised, control is transferred to the nearest catch statement.

unchecked

unchecked { statement }

Overflows in unchecked statements are ignored.

unsafe

unsafe type

Type can contain unsafe code, such as pointers. Also requires the unsafe code compiler option.

 

unsafe return_type method

The method can contain unsafe code such as pointers. Also requires the unsafe code compiler option.

using

using identifier

The using keyword makes the specified namespace implicit.

 

using (identifier_constructor_statement) statement

IDisposable.Dispose, which is the explicit destructor, is called on the named object after the statement is executed.

virtual

virtual method

Makes the method overridable in a derived class.

void

void method

A void return means that the method does not return a value. The method can omit a return statement or have an empty return.

 

void *identifier

Identifier is a pointer name. A void pointer is a typeless pointer; supported only in unsafe mode.

volatile

volatile fieldname

Access to volatile fields is serialized. This is especially useful in a multi-threaded environment. In addition, volatile fields also are not optimized.

while

while (expression) statement

The statement is repeated while the expression is true.

Primitives

Primitives are the predefined data types that are intrinsic to C#. Primitives are also keywords. Primitives historically found in C-base languages, including int, long, and many others, are included in C#. The intrinsic types are declared as C# keywords but are aliases for types in the .NET FCL. Except for the string type, the primitives are value types and allocated on the stack as structures. The string type is a class and allocated on the managed heap.

The primitives are listed in Table 1-13. Primitives have a published interface. For numeric types, the min property, max property, and Parse methods of the interface are particularly useful. The min and max property are invaluable for bounds checking, whereas the Parse method converts a string to the target primitive.

Table 1-13. Primitives in C#

Type

Primitive

Description

Range

bool

System.Boolean

Boolean

true or false.

byte

System.Byte

8-bit integer

0 to 255.

char

System.Char

16-bit Unicode character

/u0000 to /uFFFF.

decimal

System.Decimal

128-bit decimal

0 and ±1.0 × 10-28 to ±7.9 × 1028, with 28 digits of precision.

double

System.Double

64-bit floating point

0 and ±5.0 × 10-324 to ±1.7 × 10308, with 15 digits of precision.

float

System.Single

32-bit floating point

0 and ±1.5 × 10-45 to ±3.4 × 1038, with 7 digits of precision.

int

System.Int32

32-bit unsigned integer

–2,147,483,648 to 2,147,483,647.

long

System.Int64

64-bit integer

–9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.

sbyte

System.SByte

8-bit integer

–128 to 127.

short

System.Int16

16-bit integer

–32,768 to 32,767.

string

System.String

not applicable

String is an immutable variable length string.

uint

System.UInt32

32-bit unsigned integer

0 to 4,294,967,295.

ulong

System.UInt64

64-bit unsigned integer

0 to 18,446,744,073,709,551,615.

ushort

System.UInt16

16-bit unsigned integer

0 to 65,535.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.228.78