Recipe13.11.Dynamically Invoking Members


Recipe 13.11. Dynamically Invoking Members

Problem

You have a list of method names that you wish to invoke dynamically within your application. As your code executes, it will pull names off this list and attempt to invoke these methods. This technique might be useful to create a test harness for components that reads in the methods to execute from an XML file and executes them with the given parameters.

Solution

The TestDynamicInvocation method shown in Example 13-4 calls the DynamicInvocation method, which opens the XML configuration file, reads out the test information, and executes each test method dynamically.

Example 13-4. Invoking members dynamically

 public static void TestExecuteTests() {     ExecuteTests(@"..\..\SampleClassLibrary\SampleClassLibraryTests.xml",                  @"SampleClassLibrary.dll"); } public static void ExecuteTests(string xmlFile, string path) {     // Read in the methods to run from the XML file.     XmlDocument doc = new XmlDocument();     doc.Load(xmlFile);     // Get the tests to run.     XmlNodeList nodes = doc.SelectNodes(@"Tests/Test");     // Run each test method.     foreach(XmlNode node in nodes)     {         // Get the name of the type from the className attribute on Test.         string typeName = node.Attributes.GetNamedItem("className").Value;         // Get the name of the method from the methodName attribute on Test.         string methodName = node.Attributes.GetNamedItem("methodName").Value;         // Get all the parameter types.         int index = 0;         object[] parameters = new object[node.ChildNodes.Count];         foreach(XmlNode n in node.ChildNodes)         {             parameters[index] = n.InnerText;             index++;         }         object obj = InvokeMethod(path, typeName, methodName,                               node.ChildNodes.Count, parameters);         // Print out the return.         Console.WriteLine("\tReturned object: " + obj);         Console.WriteLine("\tReturned object: " + obj.GetType().FullName);     } } 

The XML document in which the test method information is contained looks like this:

 <?xml version="1.0" encoding="utf-8" ?> <Tests>     <Test className='SampleClassLibrary.SampleClass' methodName='TestMethod1'>         <Parameter>Running TestMethod1</Parameter>     </Test>     <Test className='SampleClassLibrary.SampleClass' methodName='TestMethod2'>         <Parameter>Running TestMethod2</Parameter>         <Parameter>27</Parameter>     </Test> </Tests> 

InvokeMethod, shown in Example 13-5,dynamically invokes the method that is passed to it using the information contained in the XmlNode. The parameter's types are determined by examining the ParameterInfo items on the MethodInfo, and then the values provided are converted to the actual type from a string via the Convert. ChangeType method. Finally, the return value of the invoked method is returned by this method.

Example 13-5. InvokeMethod method

 public static object InvokeMethod(string asmPath, string typeName,                                   string methodName, int paramCount,                                   object[] parameters) {     // Load the assembly.     Assembly asm = Assembly.LoadFrom(asmPath);     // Create the actual type.     Type dynClassType = asm.GetType(typeName, true, false);     // Create an instance of this type and verify that it exists     object dynObj = Activator.CreateInstance(dynClassType);     if (dynObj != null)     {         // Verify that the method exists and get its MethodInfo obj.         MethodInfo invokedMethod = dynClassType.GetMethod(methodName);         if (invokedMethod != null)         {             // Create the parameter list for the dynamically invoked methods.             int index = 0;             // For each parameter, add it to the list.             foreach (object parameter in parameters)             {                 // Get the type of the parameter.                 Type paramType =                     invokedMethod.GetParameters()[index].ParameterType;                 // Change the value to that type and assign it.                 parameters[index] =                     Convert.ChangeType(parameter, paramType);                 index++;             }             // Invoke the method with the parameters.             object retObj = invokedMethod.Invoke(dynObj, parameters);             // Return the returned object.             return (retObj);         }     }     return (null); } 

These are the dynamically invoked methods located on the SampleClass type in the SampleClassLibrary assembly:

 public bool TestMethod1(string text) {     Console.WriteLine(text);     return (true); } public bool TestMethod2(string text, int n) {     Console.WriteLine(text + " invoked with {0}",n);     return (true); } 

The output from these methods looks like this:

 Running TestMethod1         Returned object: True         Returned object: System.Boolean Running TestMethod2 invoked with 27         Returned object: True         Returned object: System.Boolean 

Discussion

Reflection gives you the ability to dynamically invoke both static and instance methods within a type in either the same assembly or in a different one. This can be a very powerful tool to allow your code to determine at runtime which method to call. This determination can be based on an assembly name, a type name, or a method name, though the assembly name is not required if the method exists in the same assembly as the invoking code, if you already have the Assembly object, or if you have a Type object for the class the method is on.

This technique may seem similar to delegates since both can dynamically determine at runtime which method is to be called. Delegates, on the whole, require you to know signatures of methods you might call at runtime, whereas with reflection, you can invoke methods when you have no idea of the signature, providing a much looser binding. More dynamic invocation can be achieved with Delegate. DynamicInvoke, but this is more of a reflection-based method than the traditional delegate invocation.

The InvokeMethod method shown in the Solution section contains all the code required to dynamically invoke a method. This code first loads the assembly using its assembly name (passed in through the asmPath parameter). Next, it gets the Type object for the class containing the method to invoke (the class name is gotten from the Test element's className attribute). The method name is then retrieved (from the Test element's methodName attribute). Once you have all of the information from the Test element, an instance of the Type object is created, and you then invoke the specified method on this created instance:

  • First, the static Activator.CreateInstance method is called to actually create an instance of the Type object contained in the local variable dynClassType. The method returns an object reference to the instance of type that was created or throws an exception if the object cannot be created.

  • Once you have successfully obtained the instance of this class, the MethodInfo object of the method to be invoked is acquired through a call to GetMethod on the Type object.

The instance of the object created with the CreateInstance method is then passed as the first parameter to the MethodInfo.Invoke method. This method returns an object containing the return value of the invoked method. This object is then returned by InvokeMethod. The second parameter to MethodInfo.Invoke is an object array containing any parameters to be passed to this method. This array is constructed based on the number of Parameter elements under each Test element in the XML. You then look at the ParameterInfo of each parameter (gotten from MethodInfo.GetParameters( )) and use the Convert.ChangeType method to coerce the string value from the XML to the proper type.

The ExecuteTests method finally displays each returned object value and its type. Note that there is no extra logic required to return different return values from the invoked methods since they are all returned as an object, unlike passing differing arguments to the invoked methods.

See Also

See the "Activator Class," "MethodInfo Class," "Convert.ChangeType Method," and "ParameterInfo Class" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

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