// DynamicMethods.cs // // ************************ // (c) 2001 Luke Venediger // // For Comments, Feedback and good South African Beer // please contact me on the address below: // lukev123@hotmail.com // ************************ // // This application will demonstrate a powerful feature of // the C# reflection libraries: Dynamic Method Invocation. // In a nutshell, the object will discover methods on-the-fly, // and will not maintain a list of the methods that it has // available. // // This is best demonstrated with the example, which is // an ordering terminal for "Bob's Burger Barn". The user // types in a command such as "drink" to order a drink, and // the object will do a method name look-up, checking whether // the method exists. If it exists, it asks the Reflection classes // to invoke the method on the current object. using System; // The following libary is necessary for reflection functionality using System.Reflection; public class DynamicMethods { // Constructor public DynamicMethods() { // Bob's Burger Barn message Console.WriteLine("\n\n---- Welcome to Bob's Burger Barn! ----"); Console.WriteLine("\n--- Type \"list\" to see a list of commands ---\n"); // The GetCommand method returns true until the user // indicates that he wants to quit, so we loop until // the method returns false. while(GetCommand()); } // Purpose: Get's an instruction from the // user, and then tries to invoke the // requested method. Each time the method runs it // will return true for a non-quit command, or // false if the user indicates his desire to quit. private bool GetCommand() { // The first string will hold the method we are looking for. // The second string will hold the possible parameters for // the method we will invoke. string userCommand; string[] userParameters = null; // This is null, just incase there are no parameters. // Prompt the user for a command, and read it into a string Console.Write("\n[?] (Type \"q\" to quit) Command: "); string userInput = Console.ReadLine(); // Break the string up into a parameterized array of strings. Our // seperator character is a space. string[] list = userInput.Split(new Char[] {' '}); Console.WriteLine(""); // Take the first parameter and store as the method // we are looking for. Also, make the string uppercase. userCommand = list[0].ToUpper(); // Check if the user wants to quit. if(userCommand == "Q") return false; // Note that we need to check if there are any parameters // before trying to seperate them into a parameter array. // So, we check the length of the original string parameter array. if(list.Length > 1) { // Create a new array bound to userParameters, with // a length 1 less than the total parameter length userParameters = new string[list.Length - 1]; // Grab the parameters into userParameters for(int counter = 1; counter < list.Length; counter++) userParameters[counter - 1] = list[counter]; } // We will hold the Reflection processing in a try-catch block // This allows us to check for parameter shortages when methods // are called. try { // The first object we need is a reflection of the // current object, encapsulated in a "Type" object. Type thisType = this.GetType(); // Now we look for the method in question, using the // Type.GetMethod(string) method, which will return a // MethodInfo representation object, or null if no matching // method was found MethodInfo theMethod = thisType.GetMethod(userCommand); // Here we check if the method exists, and if it is a "good" // method, using our own CheckMethod method. It makes sure that // the client does not call methods inherited from Object or methods // that we have in this class that we don't want called. if(theMethod == null || (!CheckMethod(theMethod))) { Console.WriteLine("[e] Command <" + userCommand + "> not supported."); return true; } // Now, using the MethodInfo object, we try to invoke the method. Two parameters // are passed: The object that we will invoke the method on, and an object array // of parameters that are to be passed to the method. theMethod.Invoke(this, userParameters); } catch (ArgumentNullException e) { // This exception is from the user entering in a null string on the command line Console.WriteLine("[e] Please enter in a non-null string. (" + e.Message + ")"); } catch (TargetParameterCountException e) { // This exception is thrown when the method is not passed the right number of parameters Console.WriteLine("[e] Command <" + userCommand + "> requires parameters. (" + e.Message + ")"); } catch (Exception e) { // All other exceptions! Console.WriteLine("[e] General Exception:\n" + e); } // The user did not ask to quit, so we return true. return true; } // Display's general help to the user. Note that here I have not // made the helptext dynamic. Do you have a way of implementing it? // Mail me your solution! public void HELP() { Console.WriteLine("To order a burger:\n\tburger "); Console.WriteLine("To get a drink:\n\tdrink "); Console.WriteLine("To get the name of your waiter:\n\twaiter"); Console.WriteLine("To list all of the commands you can use:\n\tlist"); } // Makes the user a burger... (NB: Method takes three seperate parameters) public void BURGER(string bun, string sauce, string meatType) { Console.WriteLine("Burger is ready, with the following config: "); Console.WriteLine("\tBun: " + bun); Console.WriteLine("\tSauce: " + sauce); Console.WriteLine("\tmeatType: " + meatType); } // Brings the user a drink public void DRINK(string drinkType) { Console.WriteLine("Here is your " + drinkType + ", sir."); } // Tells the user who the waiter is public void WAITER() { Console.WriteLine("Your waiter today is: Jack."); } // Returns a dynamic list of all // the methods in this class public void LIST() { Console.WriteLine("[i] All Commands: "); // Get an array of MethodInfo objects MethodInfo[] methods = this.GetType().GetMethods(); // Loop through the MethodInfo array, displaying the ".Name" // property for each method that passes through CheckMethod. for(int counter = 0; counter < methods.Length; counter++) // Make sure we can display this method name if(CheckMethod(methods[counter])) Console.WriteLine("\t" + methods[counter].Name); } // This method makes sure that the method being called by the // user is Public, Not Static and Not Inherited. This is all done // by checking properties of the reflection method description // that is encapsulated in the MethodInfo object. private bool CheckMethod(MethodInfo method) { // Make sure it's public if(!method.IsPublic) { return false; } // Make sure it's not static if(method.IsStatic) { return false; } // To check if the method is inherited, we get the Type of the // object that the method originates from, and check it against // the type for our own DynamicMethods object. If they don't match // then the method is from and inherited object. if(method.DeclaringType != this.GetType()) { return false; } // If we get here then we have passed // the test. return true; } // Driver method public static void Main() { // Create a new instance of our DynamicMethods class. new DynamicMethods(); } }