Working with Reflection


This next section provides an overview of reflection and how it works. In addition, this section provides examples of using reflection to query information at runtime.

Introduction to Reflection

As mentioned in this chapter's introduction, reflection is a technology that allows managed code to inspect various aspects of the code and metadata at runtime. This allows managed code to do simple things such as obtaining a list of the properties belonging to an object instance, or even more complex things such as querying custom attributes or obtaining an image from a resource embedded within an assembly.

How Reflection Works

One of the main keys to making reflection work is understanding that data types are objects. Understanding this point is crucial to understanding reflection. Managed code can obtain an object instance that is a System.Type. Because you can treat a data type as an object, you can execute methods and query properties of that data type. Methods on the System.Type class enable you to query the list of members belonging to that type or a list of attributes, methods, and so on.

One of the main differences between a managed environment and an unmanaged environment is that the .NET managed environment not only manages the in-memory instances, but the Common Language Runtime actually knows the data type information about each instance it is managing.

With a traditional, unmanaged language, data is stored in memory. If your code creates an array of 16-bit integers that is two elements long, there could be a sequential set of four bytes that represents that array. With this unmanaged language, there is no real information about what kind of data is represented by that array; the only information about the code is contained in the code itself. With a managed environment, like the .NET Framework, not only would the array data be stored, but the data type of the array would also be stored.

For example, when you create an instance of a class Porsche, the Common Language Runtime is managing that class. At runtime, without having access to the source code that generated that class, managed code can inspect Porsche and determine that it inherits from SportsCar, which in turn inherits from Car. If the only information maintained in the Porsche class is a simple integer, then, in an unmanaged environment, a Porsche that inherited from SportsCar that inherited from Car would look identical to an integer representing a simple loop counter.

By maintaining reflection information at runtime, managed code becomes extremely powerful and has several advantages over its unmanaged counterparts.

Discovering Type Information at Runtime

You probably do some things in your code quite often without realizing that you are using reflection or capabilities provided by reflection. Take a look at the following code snippet :

    if (theCar is Porsche)    {        // do something    } 

This code performs some action if the variable theCar is of the type Porsche. The is keyword not only checks the instantiation type, but also checks the types of all inherited ancestors.

THE is OPERATOR

The is operator checks the entire lineage of an object hierarchy. It returns true if the variable can be cast to the destination type without exception. This means that it returns true for the object's instantiation type, the type of its parent, and its ancestors. To make an explicit check on the instantiation type, use the typeof operator and get the type's fully qualified name.


The typeof operator is another keyword that you might have used in reflection and not known that you were doing so. When applied to an object instance, typeof returns the System.Type corresponding to that object, as shown in the following code line:

 System.Type carType = typeof(myPorsche); 

As you can see, the typeof operator returns an instance of the System.Type class. After you have obtained an instance of the System.Type class using any of several different methods, you can accomplish quite a few things. Table 11.1 lists some of the more commonly used methods and properties of the System.Type class.

Table 11.1. Commonly Used Methods and Properties of System.Type

Method / Property

Description

Assembly

Gets the assembly to which the type belongs

Attributes

Gets the attributes associated with the type (discussed later)

BaseType

Gets the type from which the class inherits as a direct parent

FullName

Gets the fully qualified name of the data type

IsAbstract

Indicates whether the type is an abstract type

IsClass

Indicates whether the type is a class

IsInterface

Indicates whether the type is an interface

Namespace

Gets the namespace of the data type

GetConstructors()

Returns a list of constructors for this type

GetEvents()

Gets a list of events declared or inherited by the type

GetMember()

Gets a specified member of the type

GetMembers()

Gets all the members defined by the type

GetProperty()

Gets a specific property of the type

GetProperties()

Gets all the properties defined by the type


Read the code in Listing 11.1 and the explanation that follows. Listing 11.1 provides a sample of using reflection to obtain information about a class at runtime.

Listing 11.1. Reflection Example
 using System; namespace ReflectionSample {   public class Car    {     private int numWheels = 4;     public Car()     {     }     public int NumWheels     {     get     {       return numWheels;     }     set     {       numWheels = value;     }   }   public void PrintWheels()   {     Console.WriteLine(numWheels);   }   }   public class SportsCar : Car   {    private string color = string.Empty;    public SportsCar()    {    }    public string Color    {      get      {        return color;      }      set      {        color = value;      }    }    public void PrintColor()    {      Console.WriteLine(color);    }   }   public class Porsche : SportsCar   {     private int someValue = 12;     public Porsche()     {     }     public int SomeValue     {       get       {         return someValue;       }       set       {         someValue = value;       }     }     public void PrintValue()     {       Console.WriteLine(someValue);     }   }   class Class1   {     [STAThread]     static void Main(string[] args)     {       Porsche myPorsche = new Porsche();       Type carType = myPorsche.GetType();       // list all the methods of carType        MethodInfo[] methods = carType.GetMethods();       foreach (MethodInfo method in methods)       {         Console.WriteLine(method.Name);       }       Console.WriteLine("--");       // list all the properties        PropertyInfo[] properties = carType.GetProperties();       foreach (PropertyInfo property in properties)       {         Console.WriteLine(property.Name);       }       Console.WriteLine("--");       // get the inheritance tree        Type parent = carType.BaseType;       Console.Write(carType.Name + " ->");       while (parent != null )       {         Console.Write(" -> " + parent.Name);         parent = parent.BaseType;       }       Console.ReadLine();     }   } } 

This sample starts by creating a hierarchy of classes , a Porsche class that inherits from SportsCar, which in turn inherits from Car. Each of these classes defines a property and a method. By now, having covered the basics of object-oriented programming in C#, you should be familiar with these types.

The first thing the example does with Reflection is to list all the different methods of the carType object. If you run the example, you will see that there are some methods you might not have expectedthese are methods that belong to the System.Object class.

The second piece of code illustrates how to get a list of all the properties that belong to a given type using the GetProperties() method. This method has some optional parameters that enable you to filter the list of properties, just as there are optional parameters to the GetMembers() method.

Finally, code that many programmers find the most interesting shows how to traverse an object's hierarchy and obtain all of its parents and ancestors. This section of code produces the following output:

 Porsche -> SportsCar -> Car -> Object 

This output further reinforces the notion that all classes in the .NET Framework inherit from System.Object.



    Visual C#. NET 2003 Unleashed
    Visual C#. NET 2003 Unleashed
    ISBN: 672326760
    EAN: N/A
    Year: 2003
    Pages: 316

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