Using Reflection


Using Type’s methods and properties, it is possible to obtain detailed information about a type at runtime. This is an extremely powerful feature, because once you have obtained information about a type, you can invoke its constructors, call its methods, and use its properties. Thus, reflection enables you to use code that was not available at compile time.

The Reflection API is quite large, and it is not possible to cover the entire topic here. (Complete coverage of reflection could easily fill an entire book!) However, because the Reflection API is logically designed, once you understand how to use a part of it, the rest just falls into place. With this thought in mind, the following sections demonstrate four key reflection techniques: obtaining information about methods, invoking methods, constructing objects, and loading types from assemblies.

Obtaining Information About Methods

Once you have a Type object, you can obtain a list of methods supported by the type by using GetMethods( ). One form is shown here:

  • MethodInfo[ ] GetMethods( )

It returns an array of MethodInfo objects that describe the methods supported by the invoking type. MethodInfo is in the System.Reflection namespace.

MethodInfo is derived from the abstract class MethodBase, which inherits MemberInfo. Thus, the properties and methods defined by all three of these classes are available for your use. For example, to obtain the name of a method, use the Name property. Two members that are of particular interest at this time are ReturnType and GetParameters( ).

The return type of a method is found in the read-only ReturnType property, which is an object of Type.

The method GetParameters( ) returns a list of the parameters associated with a method. It has this general form:

  • ParameterInfo[ ] GetParameters( );

The parameter information is held in a ParameterInfo object. ParameterInfo defines a large number of properties and methods that describe the parameter. Two properties that are of particular value are Name, which is a string that contains the name of the parameter, and ParameterType, which describes the parameter’s type. The parameter’s type is encapsulated within a Type object.

Here is a program that uses reflection to obtain the methods supported by a class called MyClass. For each method, it displays the return type and name of the method, and the name and type of any parameters that each method may have.

 // Analyze methods using reflection. using System; using System.Reflection; class MyClass {   int x;   int y;   public MyClass(int i, int j) {     x = i;     y = j;   }   public int sum() {     return x+y;   }   public bool isBetween(int i) {     if(x < i && i < y) return true;     else return false;   }   public void set(int a, int b) {     x = a;     y = b;   }   public void set(double a, double b) {     x = (int) a;     y = (int) b;   }   public void show() {     Console.WriteLine(" x: {0}, y: {1}", x, y);   } } class ReflectDemo {   public static void Main() {     Type t = typeof(MyClass); // get a Type object representing MyClass     Console.WriteLine("Analyzing methods in " + t.Name);     Console.WriteLine();     Console.WriteLine("Methods supported: ");     MethodInfo[] mi = t.GetMethods();     // Display methods supported by MyClass.     foreach(MethodInfo m in mi) {       // Display return type and name.       Console.Write("  " + m.ReturnType.Name +                      " " + m.Name + "(");       // Display parameters.       ParameterInfo[] pi = m.GetParameters();       for(int i=0; i < pi.Length; i++) {         Console.Write(pi[i].ParameterType.Name +                       " " + pi[i].Name);         if(i+1 < pi.Length) Console.Write(", ");       }       Console.WriteLine(")");              Console.WriteLine();     }   } }

The output is shown here:

 Analyzing methods in MyClass Methods supported:    Int32 sum()        Boolean isBetween(Int32 i)        Void set(Int32 a, Int32 b)        Void set(Double a, Double b)        Void show()        Type GetType()        String ToString()        Boolean Equals(Object obj)        Int32 GetHashCode()

Notice that in addition to the methods defined by MyClass, the methods defined by object are also displayed. This is because all types in C# inherit object. Also notice that the .NET structure names are used for the type names. Observe that set( ) is displayed twice. This is because set( ) is overloaded. One version takes int arguments. The other takes double arguments.

Let’s look at this program closely. First, notice that MyClass defines a public constructor and a number of public methods, including the overloaded set( ) method.

Inside Main( ), a Type object representing MyClass is obtained using this line of code:

 Type t = typeof(MyClass); // get a Type object representing MyClass

Using t and the Reflection API, the program then displays information about the methods supported by MyClass. First, a list of the methods is obtained by the following statement:

 MethodInfo[] mi = t.GetMethods();

Next, a foreach loop is established that cycles through mi. With each pass, the return type, name, and parameters for each method are displayed by the following code:

 // Display return type and name. Console.Write("   " + m.ReturnType.Name +               " " + m.Name + "("); // Display parameters. ParameterInfo[] pi = m.GetParameters(); for(int i=0; i < pi.Length; i++) {   Console.Write(pi[i].ParameterType.Name +                 " " + pi[i].Name);   if(i+1 < pi.Length) Console.Write(", "); }

In this sequence, the parameters associated with each method are obtained by calling GetParameters( ) and are stored in the pi array. Then a for loop cycles through the pi array, displaying the type and name of each parameter. The key point is that this information is obtained dynamically at runtime without relying on prior knowledge of MyClass.

A Second Form of GetMethods( )

A second form of GetMethods( ) lets you specify various flags that filter the methods that are retrieved. It has this general form:

  • MethodInfo[ ] GetMethods(BindingFlags flags)

This version obtains only those methods that match the criteria that you specify. BindingFlags is an enumeration. Here are several commonly used values:

Value

Meaning

DeclaredOnly

Retrieves only those methods defi ned by the specifi ed class. Inherited methods are not included.

Instance

Retrieves instance methods.

NonPublic

Retrieves nonpublic methods.

Public

Retrieves public methods.

Static

Retrieves static methods.

You can OR together two or more flags. In fact, minimally you must include either Instance or Static with Public or NonPublic. Failure to do so will result in no methods being retrieved.

One of the main uses of the BindingFlags form of GetMethods( ) is to enable you to obtain a list of the methods defined by a class without also retrieving the inherited methods. This is especially useful for preventing the methods defined by object from being obtained. For example, try substituting this call to GetMethods( ) into the preceding program:

 // Now, only methods declared by MyClass are obtained. MethodInfo[] mi = t.GetMethods(BindingFlags.DeclaredOnly |                                BindingFlags.Instance |                                BindingFlags.Public) ;

After this change is made, the program produces the following output:

 Analyzing methods in MyClass Methods supported:    Int32 sum()        Boolean isBetween(Int32 i)        Void set(Int32 a, Int32 b)        Void set(Double a, Double b)        Void show()

As you can see, only those methods explicitly defined by MyClass are displayed.

Calling Methods Using Reflection

Once you know what methods a type supports, you can call one or more of them. To do this, you will use the Invoke( ) method that is contained in MethodInfo. One of its forms is shown here:

  • object Invoke(object ob, object[ ] args)

Here, ob is a reference to the object on which the method is invoked. For static methods, ob must be null. Any arguments that need to be passed to the method are specified in the array args. If no arguments are needed, args must be null. Also, args must contain exactly the same number of elements as there are arguments. Therefore, if two arguments are needed, then args must be two elements long. It can’t, for example, be three or four elements long. The value returned by the invoked method is returned by Invoke( ).

To call a method, simply call Invoke( ) on an instance of MethodInfo that was obtained by calling GetMethods( ). The following program demonstrates the procedure:

 // Invoke methods using reflection. using System; using System.Reflection; class MyClass {   int x;   int y;   public MyClass(int i, int j) {     x = i;     y = j;   }   public int sum() {     return x+y;   }   public bool isBetween(int i) {     if((x < i) && (i < y)) return true;     else return false;   }   public void set(int a, int b) {     Console.Write("Inside set(int, int). ");     x = a;     y = b;     show();   }   // Overload set.   public void set(double a, double b) {     Console.Write("Inside set(double, double). ");     x = (int) a;     y = (int) b;     show();   }   public void show() {     Console.WriteLine("Values are x: {0}, y: {1}", x, y);   } } class InvokeMethDemo {   public static void Main() {     Type t = typeof(MyClass);     MyClass reflectOb = new MyClass(10, 20);     int val;     Console.WriteLine("Invoking methods in " + t.Name);     Console.WriteLine();     MethodInfo[] mi = t.GetMethods();     // Invoke each method.     foreach(MethodInfo m in mi) {       // Get the parameters.       ParameterInfo[] pi = m.GetParameters();       if(m.Name.CompareTo("set")==0 &&          pi[0].ParameterType == typeof(int)) {         object[] args = new object[2];         args[0] = 9;         args[1] = 18;         m.Invoke(reflectOb, args);       }       else if(m.Name.CompareTo("set")==0 &&          pi[0].ParameterType == typeof(double)) {         object[] args = new object[2];         args[0] = 1.12;         args[1] = 23.4;         m.Invoke(reflectOb, args);       }       else if(m.Name.CompareTo("sum")==0) {         val = (int) m.Invoke(reflectOb, null);         Console.WriteLine("sum is " + val);       }       else if(m.Name.CompareTo("isBetween")==0) {         object[] args = new object[1];         args[0] = 14;         if((bool) m.Invoke(reflectOb, args))           Console.WriteLine("14 is between x and y");       }       else if(m.Name.CompareTo("show")==0) {         m.Invoke(reflectOb, null);       }     }   } }

The output is shown here:

 Invoking methods in MyClass sum is 30 14 is between x and y Inside set(int, int). Values are x: 9, y: 18 Inside set(double, double). Values are x: 1, y: 23 Values are x: 1, y: 23

Look closely at how the methods are invoked. First, a list of methods is obtained. Then, inside the foreach loop, parameter information is retrieved. Next, using a series of if-else statements, each method is executed with the proper type and number of arguments. Pay special attention to the way that the overloaded set( ) method is executed by the following code:

 if(m.Name.CompareTo("set")==0 &&    pi[0].ParameterType == typeof(int)) {   object[] args = new object[2];   args[0] = 9;   args[1] = 18;   m.Invoke(reflectOb, args); } else if(m.Name.CompareTo("set")==0 &&   pi[0].ParameterType == typeof(double)) {   object[] args = new object[2];   args[0] = 1.12;   args[1] = 23.4;   m.Invoke(reflectOb, args); }

If the name of the method is set, then the type of the first parameter is tested to determine which version of the method was found. If it was set(int, int), then int arguments are loaded into args and set( ) is called. Otherwise, double arguments are used.

Obtaining a Type’s Constructors

In the previous example, there is no advantage to using reflection to invoke methods on MyClass since an object of type MyClass was explicitly created. It would be easier to just call its methods normally. However, the power of reflection starts to become apparent when an object is created dynamically at runtime. To do this, you will need to first obtain a list of the constructors. Then you will create an instance of the type by invoking one of the constructors. This mechanism allows you to instantiate any type of object at runtime without naming it in a declaration statement.

To obtain the constructors for a type, call GetConstructors( ) on a Type object. One commonly used form is shown here:

  • ConstructorInfo[ ] GetConstructors( )

It returns an array of ConstructorInfo objects that describe the constructors.

ConstructorInfo is derived from the abstract class MethodBase, which inherits MemberInfo. It also defines several members of its own. The one we are interested in is GetParameters( ), which returns a list of the parameters associated with a constructor. It works just like GetParameters( ) defined by MethodInfo, described earlier.

Once an appropriate constructor has been found, an object is created by calling the Invoke( ) method defined by ConstructorInfo. One form is shown here:

  • object Invoke(object[ ] args)

Any arguments that need to be passed to the constructor are specified in the array args. If no arguments are needed, args must be null. Also, args must contain exactly the same number of elements as there are arguments. Invoke( ) returns a reference to the object that was constructed.

The following program uses reflection to create an instance of MyClass:

 // Create an object using reflection. using System; using System.Reflection; class MyClass {   int x;   int y;   public MyClass(int i) {     Console.WriteLine("Constructing MyClass(int, int). ");     x = y = i;   }   public MyClass(int i, int j) {     Console.WriteLine("Constructing MyClass(int, int). ");     x = i;     y = j;     show();   }   public int sum() {     return x+y;   }   public bool isBetween(int i) {     if((x < i) && (i < y)) return true;     else return false;   }   public void set(int a, int b) {     Console.Write("Inside set(int, int). ");     x = a;     y = b;     show();   }   // Overload set.   public void set(double a, double b) {     Console.Write("Inside set(double, double). ");     x = (int) a;     y = (int) b;     show();   }   public void show() {     Console.WriteLine("Values are x: {0}, y: {1}", x, y);   } } class InvokeConsDemo {   public static void Main() {     Type t = typeof(MyClass);     int val;     // Get constructor info.     ConstructorInfo[] ci = t.GetConstructors();     Console.WriteLine("Available constructors: ");     foreach(ConstructorInfo c in ci) {       // Display return type and name.       Console.Write("   " + t.Name + "(");       // Display parameters.       ParameterInfo[] pi = c.GetParameters();       for(int i=0; i < pi.Length; i++) {         Console.Write(pi[i].ParameterType.Name +                       " " + pi[i].Name);         if(i+1 < pi.Length) Console.Write(", ");       }       Console.WriteLine(")");     }     Console.WriteLine();     // Find matching constructor.     int x;     for(x=0; x < ci.Length; x++) {       ParameterInfo[] pi =  ci[x].GetParameters();       if(pi.Length == 2) break;     }     if(x == ci.Length) {       Console.WriteLine("No matching constructor found.");       return;     }     else       Console.WriteLine("Two-parameter constructor found.\n");     // Construct the object.     object[] consargs = new object[2];     consargs[0] = 10;     consargs[1] = 20;     object reflectOb = ci[x].Invoke(consargs);     Console.WriteLine("\nInvoking methods on reflectOb.");     Console.WriteLine();     MethodInfo[] mi = t.GetMethods();     // Invoke each method.     foreach(MethodInfo m in mi) {       // Get the parameters.       ParameterInfo[] pi = m.GetParameters();       if(m.Name.CompareTo("set")==0 &&          pi[0].ParameterType == typeof(int)) {         // This is set(int, int).         object[] args = new object[2];         args[0] = 9;         args[1] = 18;         m.Invoke(reflectOb, args);       }       else if(m.Name.CompareTo("set")==0 &&               pi[0].ParameterType == typeof(double)) {         // This is set(double, double).         object[] args = new object[2];         args[0] = 1.12;         args[1] = 23.4;         m.Invoke(reflectOb, args);       }       else if(m.Name.CompareTo("sum")==0) {         val = (int) m.Invoke(reflectOb, null);         Console.WriteLine("sum is " + val);       }       else if(m.Name.CompareTo("isBetween")==0) {         object[] args = new object[1];         args[0] = 14;         if((bool) m.Invoke(reflectOb, args))           Console.WriteLine("14 is between x and y");       }       else if(m.Name.CompareTo("show")==0) {         m.Invoke(reflectOb, null);       }     }   } }

The output is shown here:

 Available constructors:    MyClass(Int32 i)    MyClass(Int32 i, Int32 j) Two-parameter constructor found. Constructing MyClass(int, int). Values are x: 10, y: 20 Invoking methods on reflectOb. sum is 30 14 is between x and y Inside set(int, int). Values are x: 9, y: 18 Inside set(double, double). Values are x: 1, y: 23 Values are x: 1, y: 23

Let’s look at how reflection is used to construct a MyClass object. First, a list of the public constructors is obtained using the following statement:

 ConstructorInfo[] ci = t.GetConstructors();

Next, for the sake of illustration, the constructors are displayed. Then the list is searched for a constructor that takes two arguments, using this code:

 for(x=0; x < ci.Length; x++) {   ParameterInfo[] pi =  ci[x].GetParameters();   if(pi.Length == 2) break; }

If the constructor is found (as it will be in this case), an object is instantiated by the following sequence:

 // Construct the object. object[] consargs = new object[2]; consargs[0] = 10; consargs[1] = 20; object reflectOb = ci[x].Invoke(consargs);

After the call to Invoke( ), reflectOb will refer to an object of type MyClass. The program then executes methods on that instance.

One important point needs to be made. In this example, for the sake of simplicity, it was assumed that the only two-argument constructor was one that took two int arguments. In a real-world application, this would have to be verified by checking the parameter type of each argument.

Obtaining Types from Assemblies

In the preceding example, everything about MyClass has been discovered using reflection except for one item: the type MyClass, itself. That is, although the preceding examples dynamically determined information about MyClass, they still relied upon the fact that the type name MyClass was known in advance and used in a typeof statement to obtain a Type object upon which all of the reflection methods either directly or indirectly operated. Although this might be useful in a number of circumstances, the full power of reflection is found when the types available to a program are determined dynamically by analyzing the contents of other assemblies.

As you know from Chapter 16, an assembly carries with it type information about the classes, structures, and so on, that it contains. The Reflection API allows you to load an assembly, discover information about it, and create instances of any of its publicly available types. Using this mechanism, a program can search its environment, utilizing functionality that might be available without having to explicitly define that functionality at compile time. This is an extremely potent, and exciting, concept. For example, you can imagine a program that acts as a “type browser,” displaying the types available on a system. Another application could be a design tool that lets you visually “wire together” a program that is composed of the various types supported by the system. Since all information about a type is discoverable, there is no inherent limitation to ways reflection can be applied.

To obtain information about an assembly, you will first create an Assembly object. The Assembly class does not define a public constructor. Instead, an Assembly object is obtained by calling one of its methods. The one that we will use is LoadFrom( ), which loads an assembly given its filename. The form we will use is shown here:

  • static Assembly LoadFrom(string filename)

Here, filename specifies the filename of the assembly.

Once you have obtained an Assembly object, you can discover the types that it defines by calling GetTypes( ) on it. Here is its general form:

  • Type[ ] GetTypes( )

It returns an array of the types contained in the assembly.

To demonstrate the discovery of types in an assembly, you will need two files. The first will contain a set of classes that will be discovered by the second. To begin, create a file called MyClasses.cs that contains the following:

 // A file that contains three classes.  Call this file MyClasses.cs. using System; class MyClass {   int x;   int y;   public MyClass(int i) {     Console.WriteLine("Constructing MyClass(int). ");     x = y = i;     show();   }   public MyClass(int i, int j) {     Console.WriteLine("Constructing MyClass(int, int). ");     x = i;     y = j;     show();   }   public int sum() {     return x+y;   }   public bool isBetween(int i) {     if((x < i) && (i < y)) return true;     else return false;   }   public void set(int a, int b) {     Console.Write("Inside set(int, int). ");     x = a;     y = b;     show();   }   // Overload set.   public void set(double a, double b) {     Console.Write("Inside set(double, double). ");     x = (int) a;     y = (int) b;     show();   }   public void show() {     Console.WriteLine("Values are x: {0}, y: {1}", x, y);   } } class AnotherClass {   string remark;   public AnotherClass(string str) {     remark = str;   }   public void show() {     Console.WriteLine(remark);   } } class Demo {   public static void Main() {     Console.WriteLine("This is a placeholder.");   } }

This file contains MyClass, which we have been using in the previous examples. It also adds a second class called AnotherClass and a third class called Demo. Thus, the assembly produced by this program will contain three classes. Next, compile this file so that the file MyClasses.exe is produced. This is the assembly that will be interrogated.

The program that will discover information about MyClasses.exe is shown here. Enter it at this time.

 /* Locate an assembly, determine types, and create    an object using reflection. */ using System; using System.Reflection; class ReflectAssemblyDemo {   public static void Main() {     int val;     // Load the MyClasses.exe assembly.     Assembly asm = Assembly.LoadFrom("MyClasses.exe");     // Discover what types MyClasses.exe contains.     Type[] alltypes = asm.GetTypes();     foreach(Type temp in alltypes)       Console.WriteLine("Found: " + temp.Name);     Console.WriteLine();     // Use the first type, which is MyClass in this case.     Type t = alltypes[0]; // use first class found     Console.WriteLine("Using: " + t.Name);     // Obtain constructor info.     ConstructorInfo[] ci = t.GetConstructors();     Console.WriteLine("Available constructors: ");     foreach(ConstructorInfo c in ci) {       // Display return type and name.       Console.Write("   " + t.Name + "(");       // Display parameters.       ParameterInfo[] pi = c.GetParameters();       for(int i=0; i < pi.Length; i++) {         Console.Write(pi[i].ParameterType.Name +                       " " + pi[i].Name);         if(i+1 < pi.Length) Console.Write(", ");       }       Console.WriteLine(")");     }     Console.WriteLine();     // Find matching constructor.     int x;     for(x=0; x < ci.Length; x++) {       ParameterInfo[] pi =  ci[x].GetParameters();       if(pi.Length == 2) break;     }     if(x == ci.Length) {       Console.WriteLine("No matching constructor found.");       return;     }     else       Console.WriteLine("Two-parameter constructor found.\n");     // Construct the object.     object[] consargs = new object[2];     consargs[0] = 10;     consargs[1] = 20;     object reflectOb = ci[x].Invoke(consargs);     Console.WriteLine("\nInvoking methods on reflectOb.");     Console.WriteLine();     MethodInfo[] mi = t.GetMethods();     // Invoke each method.     foreach(MethodInfo m in mi) {       // Get the parameters.       ParameterInfo[] pi = m.GetParameters();       if(m.Name.CompareTo("set")==0 &&          pi[0].ParameterType == typeof(int)) {         // This is set(int, int).         object[] args = new object[2];         args[0] = 9;         args[1] = 18;         m.Invoke(reflectOb, args);       }       else if(m.Name.CompareTo("set")==0 &&          pi[0].ParameterType == typeof(double)) {         // This is set(double, double).         object[] args = new object[2];         args[0] = 1.12;         args[1] = 23.4;         m.Invoke(reflectOb, args);       }       else if(m.Name.CompareTo("sum")==0) {         val = (int) m.Invoke(reflectOb, null);         Console.WriteLine("sum is " + val);       }       else if(m.Name.CompareTo("isBetween")==0) {         object[] args = new object[1];         args[0] = 14;         if((bool) m.Invoke(reflectOb, args))           Console.WriteLine("14 is between x and y");       }       else if(m.Name.CompareTo("show")==0) {         m.Invoke(reflectOb, null);       }     }   } }

The output from the program is shown here:

 Found: MyClass Found: AnotherClass Found: Demo Using: MyClass Available constructors:    MyClass(Int32 i)    MyClass(Int32 i, Int32 j) Two-parameter constructor found. Constructing MyClass(int, int). Values are x: 10, y: 20 Invoking methods on reflectOb. sum is 30 14 is between x and y Inside set(int, int). Values are x: 9, y: 18 Inside set(double, double). Values are x: 1, y: 23 Values are x: 1, y: 23

As the output shows, all three classes contained within MyClasses.exe were found. The first one, which in this case was MyClass, was then used to instantiate an object and execute methods.

The types in MyClasses.exe are discovered using this sequence of code, which is near the start of Main( ):

 // Load the MyClasses.exe assembly. Assembly asm = Assembly.LoadFrom("MyClasses.exe"); // Discover what types MyClasses.exe contains. Type[] alltypes = asm.GetTypes(); foreach(Type temp in alltypes)   Console.WriteLine("Found: " + temp.Name);

You can use such a sequence whenever you need to dynamically load and interrogate an assembly.

On a related point, an assembly need not be an exe file. Assemblies can also be contained in dynamic link library (DLL) files that use the .dll extension. For example, if you were to compile MyClasses.cs using this command line:

 csc /t:library MyClasses.cs

then the output file would be MyClasses.dll. One advantage to putting code into a DLL is that no Main( ) method is required. All exe files require an entry point, such as Main( ). This is why the Demo class contained a placeholder Main( ) method. Entry points are not required by DLLs. If you try making MyClasses.cs into a DLL, you will need to change the call to LoadFrom( ) as shown here:

 Assembly asm = Assembly.LoadFrom("MyClasses.dll");

Fully Automating Type Discovery

Before we leave the topic of reflection, one last example will be instructive. Even though the preceding program was able to fully use MyClass without explicitly specifying MyClass in the program, it still relied upon prior knowledge of the contents of MyClass. For example, it knew the names of its methods, such as set and sum. However, by using reflection it is possible to utilize a type about which you have no prior knowledge. To do this, you must discover all information necessary to construct an object and to generate method calls. Such an approach would be useful to a visual design tool, for example, because it could utilize the types available on the system.

To see how the full dynamic discovery of a type can be accomplished, consider the following example, which loads the MyClasses.exe assembly, constructs a MyClass object, and then calls all of the methods declared by MyClass, all without assuming any prior knowledge:

 // Utilize MyClass without assuming any prior knowledge. using System; using System.Reflection; class ReflectAssemblyDemo {   public static void Main() {     int val;     Assembly asm = Assembly.LoadFrom("MyClasses.exe");     Type[] alltypes = asm.GetTypes();     Type t = alltypes[0]; // use first class found     Console.WriteLine("Using: " + t.Name);     ConstructorInfo[] ci = t.GetConstructors();     // Use first constructor found.     ParameterInfo[] cpi = ci[0].GetParameters();     object reflectOb;     if(cpi.Length > 0) {       object[] consargs = new object[cpi.Length];       // initialize args       for(int n=0; n < cpi.Length; n++)         consargs[n] = 10 + n * 20;       // construct the object       reflectOb = ci[0].Invoke(consargs);     } else       reflectOb = ci[0].Invoke(null);     Console.WriteLine("\nInvoking methods on reflectOb.");     Console.WriteLine();     // Ignore inherited methods.     MethodInfo[] mi = t.GetMethods(BindingFlags.DeclaredOnly |                                    BindingFlags.Instance |                                    BindingFlags.Public) ;     // Invoke each method.     foreach(MethodInfo m in mi) {       Console.WriteLine("Calling {0} ", m.Name);       // Get the parameters.       ParameterInfo[] pi = m.GetParameters();       // Execute methods.       switch(pi.Length) {         case 0: // no args           if(m.ReturnType == typeof(int)) {             val = (int) m.Invoke(reflectOb, null);             Console.WriteLine("Result is " + val);           }           else if(m.ReturnType == typeof(void)) {             m.Invoke(reflectOb, null);           }           break;         case 1: // one arg           if(pi[0].ParameterType == typeof(int)) {             object[] args = new object[1];             args[0] = 14;             if((bool) m.Invoke(reflectOb, args))               Console.WriteLine("14 is between x and y");             else               Console.WriteLine("14 is not between x and y");           }           break;         case 2: // two args           if((pi[0].ParameterType == typeof(int)) &&              (pi[1].ParameterType == typeof(int))) {             object[] args = new object[2];             args[0] = 9;             args[1] = 18;             m.Invoke(reflectOb, args);           }           else if((pi[0].ParameterType == typeof(double)) &&                   (pi[1].ParameterType == typeof(double))) {             object[] args = new object[2];             args[0] = 1.12;             args[1] = 23.4;             m.Invoke(reflectOb, args);           }           break;       }       Console.WriteLine();     }   } }

Here is the output produced by the program:

 Using: MyClass Constructing MyClass(int). Values are x: 10, y: 10 Invoking methods on reflectOb. Calling sum Result is 20 Calling isBetween 14 is not between x and y Calling set Inside set(int, int). Values are x: 9, y: 18 Calling set Inside set(double, double). Values are x: 1, y: 23 Calling show Values are x: 1, y: 23

The operation of the program is straightforward, but a couple of points are worth mentioning. First, notice that only the methods explicitly declared by MyClass are obtained and used. This is accomplished by using the BindingFlags form of GetMethods( ). The reason for this is to prevent calling the methods inherited from object. Second, notice how the number of parameters and return type of each method are obtained dynamically. A switch statement determines the number of parameters. Within each case, the parameter type(s) and return type are checked. A method call is then constructed based on this information.




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