Chapter 17: Runtime Type ID, Reflection, and Attributes


This chapter discusses three interrelated and powerful C# features: runtime type identification, reflection, and attributes. Runtime type ID is the mechanism that lets you identify a type during the execution of a program. Reflection is the feature that enables you to obtain information about a type. Using this information, you can construct and use objects at runtime. This feature is very powerful because it lets a program add functionality dynamically, during execution. An attribute describes a characteristic of some element of a C# program. For example, you can specify attributes for classes, methods, and fields, among others. Attributes can be interrogated at runtime, and the attribute information obtained. Attributes use both runtime type identification and reflection.

Runtime Type Identification

Runtime type identification (RTTI) allows the type of an object to be determined during program execution. RTTI is useful for many reasons. For example, you can discover precisely what type of object is being referred to by a base-class reference. Another use of RTTI is to test in advance whether a cast will succeed, preventing an invalid cast exception. Runtime type identification is also a key component of reflection.

C# includes three keywords that support runtime type identification: is, as, and typeof. Each is examined in turn.

Testing a Type with is

You can determine if an object is of a certain type by using the is operator. Its general form is shown here:

  • expr is type

Here, expr is an expression whose type is being tested against type. If the type of expr is the same as, or compatible with, type, then the outcome of this operation is true. Otherwise, it is false. Thus, if the outcome is true, expr can be cast to type.

Here is an example that uses is:

 // Demonstrate is. using System; class A {} class B : A {} class UseIs {   public static void Main() {     A a = new A();     B b = new B();     if(a is A) Console.WriteLine("a is an A");     if(b is A)       Console.WriteLine("b is an A because it is derived from A");     if(a is B)       Console.WriteLine("This won't display -- a not derived from B");     if(b is B) Console.WriteLine("B is a B");     if(a is object) Console.WriteLine("a is an object");   } }

The output is shown here:

 a is an A b is an A because it is derived from A B is a B a is an object

Most of the is expressions are self-explanatory, but two may need a little discussion. First, notice this statement:

 if(b is A)   Console.WriteLine("b is an A because it is derived from A");

The if succeeds because b is an object of type B, which is derived from type A. Thus, b is compatible with A because b can be cast to A. However, the reverse is not true. When this line is executed,

 if(a is B)   Console.WriteLine("This won't display -- a not derived from B");

the if does not succeed, because a is of type A, which is not derived from B. Thus, a cannot be cast to B.

Using as

Sometimes you will want to try a cast at runtime but not raise an exception if the cast fails. To do this, use the as operator, which has this general form:

  • expr as type

Here, expr is the expression being cast to type. If the cast succeeds, then a reference to type is returned. Otherwise, a null reference is returned.

The as operator offers a streamlined alternative to is in some cases. For example, consider the following program that uses is to prevent an invalid cast from occurring:

 // Use is to avoid an invalid cast. using System; class A {} class B : A {} class CheckCast {   public static void Main() {     A a = new A();     B b = new B();     // Check to see if a can be cast to B.     if(a is B)  // if so, do the cast       b = (B) a;     else // if not, skip the cast       b = null;     if(b==null)       Console.WriteLine("Cast b = (B) a is NOT allowed.");     else       Console.WriteLine("Cast b = (B) a is allowed.");   } }

This program displays the following output:

 Cast b = (B) a is NOT allowed.

As the output shows, since a is not a B, the cast of a to B is invalid and is prevented by the if statement. However, this approach requires two steps. First, the validity of the cast must be confirmed. Second, the cast must be made. These steps can be combined into one through the use of as, as the following program shows:

 // Demonstrate as. using System; class A {} class B : A {} class CheckCast {   public static void Main() {     A a = new A();     B b = new B();     b = a as B; // cast, if possible     if(b==null)       Console.WriteLine("Cast b = (B) a is NOT allowed.");     else       Console.WriteLine("Cast b = (B) a is allowed.");   } }

Here is the output, which is the same as before:

 Cast b = (B) a is NOT allowed.

In this version, the as statement checks the validity of the cast and then, if valid, performs the cast, all in one statement.

Using typeof

Although useful in their own ways, the as and is operators simply test the compatibility of two types. Often you will need to obtain information about a type. To do this, C# supplies the typeof operator. It retrieves a System.Type object for a given type. Using this object, you can determine the type’s characteristics.

The typeof operator has this general form:

  • typeof(type)

Here, type is the type being obtained. The Type object returned encapsulates the information associated with type.

Once you have obtained a Type object for a given type, you can obtain information about it through the use of various properties, fields, and methods defined by Type. Type is a large class with many members, and a discussion is deferred until the following section, where reflection is examined. However, to briefly demonstrate Type, the following program uses three of its properties: FullName, IsClass, and IsAbstract. To obtain the full name of the type, use FullName. IsClass returns true if the type is a class. IsAbstract returns true if a class is abstract.

 // Demonstrate typeof. using System; using System.IO; class UseTypeof {   public static void Main() {     Type t = typeof(StreamReader);     Console.WriteLine(t.FullName);     if(t.IsClass) Console.WriteLine("Is a class.");     if(t.IsAbstract) Console.WriteLine("Is abstract.");     else Console.WriteLine("Is concrete.");   } }

This program outputs the following:

 System.IO.StreamReader Is a class. Is concrete.

This program obtains a Type object that describes StreamReader. It then displays the full name, determines if it is a class, and finds out whether it is abstract.




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