Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
66
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

Chapter 52: Keywords

Keywords are predefined, reserved identifiers with special meaning to the compiler. They cannot be used as identifiers in your program without the @ prefix. For example @if is a legal identifier but not the keyword if.

Section 52.1: as

The as keyword is an operator similar to a cast. If a cast is not possible, using as produces null rather than resulting in an InvalidCastException.

expression as type is equivalent to expression is type ? (type)expression : (type)null with the caveat that as is only valid on reference conversions, nullable conversions, and boxing conversions. User-defined conversions are not supported; a regular cast must be used instead.

For the expansion above, the compiler generates code such that expression will only be evaluated once and use single dynamic type check (unlike the two in the sample above).

as can be useful when expecting an argument to facilitate several types. Specifically it grants the user multiple options - rather than checking every possibility with is before casting, or just casting and catching exceptions. It is best practice to use 'as' when casting/checking an object which will cause only one unboxing penalty. Using is to check, then casting will cause two unboxing penalties.

If an argument is expected to be an instance of a specific type, a regular cast is preferred as its purpose is more clear to the reader.

Because a call to as may produce null, always check the result to avoid a NullReferenceException.

Example usage

object something = "Hello";

 

 

Console.WriteLine(something

as string);

//Hello

Console.Writeline(something

as

Nullable<int>); //null

Console.WriteLine(something

as

int?);

//null

//This does NOT compile:

//destination type must be a reference type (or a nullable value type)

Console.WriteLine(something as int);

Live Demo on .NET Fiddle

Equivalent example without using as:

Console.WriteLine(something is string ? (string)something : (string)null);

This is useful when overriding the Equals function in custom classes.

class MyCustomClass

{

public override bool Equals(object obj)

{

MyCustomClass customObject = obj as MyCustomClass;

//if it is null it may be really null

//or it may be of a different type

GoalKicker.com – C# Notes for Professionals

222

if (Object.ReferenceEquals(null, customObject))

{

// If it is null then it is not equal to this instance. return false;

}

// Other equality controls specific to class

}

}

Section 52.2: goto

goto can be used to jump to a specific line inside the code, specified by a label.

goto as a:

Label:

void InfiniteHello()

{

sayHello: Console.WriteLine("Hello!"); goto sayHello;

}

Live Demo on .NET Fiddle

Case statement:

enum Permissions { Read, Write };

switch (GetRequestedPermission())

{

case Permissions.Read: GrantReadAccess(); break;

case Permissions.Write: GrantWriteAccess();

goto case Permissions.Read; //People with write access also get read

}

Live Demo on .NET Fiddle

This is particularly useful in executing multiple behaviors in a switch statement, as C# does not support fall-through case blocks.

Exception Retry

var exCount = 0; retry:

try

{

//Do work

}

catch (IOException)

{

exCount++;

if (exCount < 3)

{

Thread.Sleep(100);

GoalKicker.com – C# Notes for Professionals

223

goto retry;

}

throw;

}

Live Demo on .NET Fiddle

Similar to many languages, use of goto keyword is discouraged except the cases below.

Valid usages of goto which apply to C#:

Fall-through case in switch statement.

Multi-level break. LINQ can often be used instead, but it usually has worse performance.

Resource deallocation when working with unwrapped low-level objects. In C#, low-level objects should usually be wrapped in separate classes.

Finite state machines, for example, parsers; used internally by compiler generated async/await state machines.

Section 52.3: volatile

Adding the volatile keyword to a field indicates to the compiler that the field's value may be changed by multiple separate threads. The primary purpose of the volatile keyword is to prevent compiler optimizations that assume only single-threaded access. Using volatile ensures that the value of the field is the most recent value that is available, and the value is not subject to the caching that non-volatile values are.

It is good practice to mark every variable that may be used by multiple threads as volatile to prevent unexpected behavior due to behind-the-scenes optimizations. Consider the following code block:

public class Example

{

public int x;

public void DoStuff()

{

x = 5;

// the compiler will optimize this to y = 15 var y = x + 10;

/* the value of x will always be the current value, but y will always be "15" */

Debug.WriteLine("x = " + x + ", y = " + y);

}

}

In the above code-block, the compiler reads the statements x = 5 and y = x + 10 and determines that the value of y will always end up as 15. Thus, it will optimize the last statement as y = 15. However, the variable x is in fact a public field and the value of x may be modified at runtime through a di erent thread acting on this field separately. Now consider this modified code-block. Do note that the field x is now declared as volatile.

public class Example

{

public volatile int x;

public void DoStuff()

GoalKicker.com – C# Notes for Professionals

224

{

x = 5;

// the compiler no longer optimizes this statement var y = x + 10;

/* the value of x and y will always be the correct values */

Debug.WriteLine("x = " + x + ", y = " + y);

}

}

Now, the compiler looks for read usages of the field x and ensures that the current value of the field is always retrieved. This ensures that even if multiple threads are reading and writing to this field, the current value of x is always retrieved.

volatile can only be used on fields within classes or structs. The following is not valid:

public void MyMethod() { volatile int x; }

volatile can only be applied to fields of following types:

reference types or generic type parameters known to be reference types

primitive types such as sbyte, byte, short, ushort, int, uint, char, float, and bool

enums types based on byte, sbyte, short, ushort, int or uint

IntPtr and UIntPtr

Remarks:

The volatile modifier is usually used for a field that is accessed by multiple threads without using the lock statement to serialize access.

The volatile keyword can be applied to fields of reference types

The volatile keyword will not make operating on 64-bit primitives on a 32-bit platform atomic. Interlocked operations such as Interlocked.Read and Interlocked.Exchange must still be used for safe multi-threaded access on these platforms.

Section 52.4: checked, unchecked

The checked and unchecked keywords define how operations handle mathematical overflow. "Overflow" in the context of the checked and unchecked keywords is when an integer arithmetic operation results in a value which is greater in magnitude than the target data type can represent.

When overflow occurs within a checked block (or when the compiler is set to globally use checked arithmetic), an exception is thrown to warn of undesired behavior. Meanwhile, in an unchecked block, overflow is silent: no exceptions are thrown, and the value will simply wrap around to the opposite boundary. This can lead to subtle, hard to find bugs.

Since most arithmetic operations are done on values that are not large or small enough to overflow, most of the time, there is no need to explicitly define a block as checked. Care needs to be taken when doing arithmetic on unbounded input that may cause overflow, for example when doing arithmetic in recursive functions or while taking user input.

Neither checked nor unchecked a ect floating point arithmetic operations.

When a block or expression is declared as unchecked, any arithmetic operations inside it are allowed to overflow without causing an error. An example where this behavior is desired would be the calculation of a checksum, where

GoalKicker.com – C# Notes for Professionals

225