Miscellaneous Keywords


To conclude Part I, the few remaining keywords defined by C# that have not been described elsewhere are briefly discussed.

lock

The lock keyword is used when creating multithreaded programs. It is examined in detail in Chapter 22, where multithreaded programming is discussed. A brief description is given here for the sake of completeness.

In C#, a program can contain two or more threads of execution. When this is the case, the program is said to be multithreaded, and pieces of the program are executed concurrently. Thus, pieces of the program execute independently, and conceptually speaking, simultaneously. This raises the prospect of a special type of problem: What if two threads try to use a resource that can be used by only one thread at a time? To solve this problem, you can create a critical code section that will be executed by one and only one thread at a time. This is accomplished by lock. Its general form is shown here:

 lock(obj) {    // critical section }

Here, obj is an object that seeks to obtain the lock. If one thread has already entered the critical section, then a second thread will wait until the first thread exits the critical section. When the lock is granted, the critical section can be executed.

Remember 

lock is discussed in detail in Chapter 22.

readonly

You can create a read-only field in a class by declaring it as readonly. A readonly field can be given a value only by using an initializer when it is declared, or by assigning it a value within a static constructor. Once the value has been set, it can’t be changed. Thus, readonly fields are a good way to create constants, such as array dimensions that are used throughout a program. Both static and non-static readonly fields are allowed.

Here is an example that creates a readonly field:

 // Demonstrate readonly. using System; class MyClass {   public static readonly int SIZE = 10; } class DemoReadOnly {   public static void Main() {     int[] nums = new int[MyClass.SIZE];     for(int i=0; i<MyClass.SIZE; i++)       nums[i] = i;     foreach(int i in nums)       Console.Write(i + " ");     // MyClass.SIZE = 100; // Error!!! can't change   } }

Here, MyClass.SIZE is initialized to 10. After that, it can be used, but not changed. To prove this, try removing the comment symbol from before the last line and then compiling the program. As you will see, an error will result.

The using Statement

In addition to the using directive discussed earlier, using has a second form that is called the using statement. It has these general forms:

 using (obj) {    // use obj } using (type obj = initializer) {   // use obj }

Here, obj is an object that is being used inside the using block. In the first form, the object is declared outside the using statement. In the second form, the object is declared within the using statement. When the block concludes, the Dispose( ) method (defined by System.IDisposable interface) will be called. The using statement applies only to objects that implement the System.IDisposable interface.

Here is an example of each form of the using statement:

 // Demonstrate using statement. using System; using System.IO; class UsingDemo {   public static void Main() {     StreamReader sr = new StreamReader("test.txt");     // Use object inside using statement.     using(sr) {       Console.WriteLine(sr.ReadLine());       sr.Close();     }     // Create StreamReader inside the using statement.     using(StreamReader sr2 = new StreamReader("test.txt")) {       Console.WriteLine(sr2.ReadLine());       sr2.Close();     }   } }

The class StreamReader implements the IDisposable interface (through its base class TextReader). Thus, it can be used in a using statement.

const and volatile

The const modifier is used to declare fields or local variables that cannot be changed. These variables must be given initial values when they are declared. Thus, a const variable is essentially a constant. For example:

 const int i = 10;

creates a const variable called i that has the value 10.

The volatile modifier tells the compiler that a field’s value may be changed in ways not explicitly specified by the program. For example, a field that holds the current system time might be updated automatically by the operating system. In this situation, the contents of the field are altered without any explicit assignment statement. The reason the external alteration of a field may be important is that the C# compiler is permitted to optimize certain expressions automatically, on the assumption that the content of a field is unchanged if it does not occur on the left side of an assignment statement. However, if factors outside the immediate code, such as a second thread of execution, change the value of the field, this assumption is wrong. By using volatile, you are telling the compiler that it must obtain the value of this field each time it is accessed.

extern

The extern keyword has two uses. Each is examined here.

Declaring extern Methods

The first use of extern has been available since the creation of C#. It indicates that a method is provided by unmanaged code that is not part of the program. In other words, that method is supplied by external code.

To declare a method as external, simply precede its declaration with the extern modifier. The declaration must not include any body. Thus, the general form of an extern declaration is shown here:

 extern ret-type meth-name(arg-list);

Notice that no braces are used.

In this use, extern is often used with the DllImport attribute, which specifies the DLL that contains the method. DllImport is in the System.Runtime.InteropServices namespace. It supports several options, but for most uses it is sufficient to simply specify the name of the DLL that contains the extern method. In general, extern methods should be coded in C. (If you use C++, then the name of the method within the DLL might be altered with the addition of type decorations.)

Declaring an extern Assembly Alias

A second form of extern was added by C# 2.0. This form provides an alias for an external assembly. It is used in cases when a program includes two separate assemblies which both contain the same name. For example, if an assembly called test1 contains a class called MyClass and another assembly called test2 also contains a class called MyClass, then a conflict will arise if both classes need to be used within the same program.

To solve this problem, you must create an alias for each assembly. This is a two-step process. First, you must specify the aliases using the /r compiler option:

 /r:Asm1=test1 /r:Asm2=test2

Second, you must specify extern statements that refer to these aliases. Here is the form of extern that creates an assembly alias:

 extern alias assembly-name;

Continuing the example, these lines must appear in your program:

 extern alias Asm1; extern alias Asm2;

Now, either version of MyClass can be accessed by qualifying it with its alias.

Here is a complete example that demonstrates an extern alias. It contains three files. The first is shown here. It should be put in a file called test1.cs.

 using System; namespace MyNS {   public class MyClass {     public MyClass() {       Console.WriteLine("Constructing from MyClass1.dll.");     }   } }

The second file is called test2.cs. It is shown here:

 using System; namespace MyNS {   public class MyClass {     public MyClass() {       Console.WriteLine("Constructing from MyClass2.dll.");     }   } }

Notice that both test1.cs and test2.cs define a namespace called MyNS and that within that namespace, both files define a class called MyClass. Thus, without an extern alias, no program could have access to both versions of MyClass.

The third file is test3.cs, shown next, and uses MyClass from both test1.cs and test2.cs. It is able to do this because of the extern alias statements.

 // extern alias statements must be at the top of the file. extern alias Asm1; extern alias Asm2; using System; class Demo {   public static void Main() {     Asm1::MyNS.MyClass t = new Asm1::MyNS.MyClass();     Asm2::MyNS.MyClass t2 = new Asm2::MyNS.MyClass();   } }

Start by compiling test1.cs and test2.cs into DLLs. This can be easily done from the command line by using these commands:

 csc /t:library test1.cs csc /t:library test2.cs

Next, compile test3.cs by using this command line:

 csc /r:Asm1=test1.dll /r:Asm2=test2.dll test3.cs

Notice the use of the /r option, which tells the compiler to reference the metadata found in the associated file. In this case, the alias Asm1 is linked with test1.dll, and the alias Asm2 is linked with test2.dll.

Within the program, the aliases are specified by these two extern statements at the top of the file:

 extern alias Asm1; extern alias Asm2;

Within Main( ), the aliases are used to disambiguate the references to MyClass. Notice how the alias is used to refer to MyClass:

 Asm1::MyNS.MyClass

The alias is specified first, followed by the namespace resolution operator, followed by the name of the namespace that contains the ambiguous class, followed by the dot operator and the class name. This same general form works with other extern aliases.

The output from the program is shown here:

 Constructing from MyClass1.dll. Constructing from MyClass2.dll.




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net