Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 407
  • Last Modified:

Running a static method dynamically

I have saved my class names and method names in a database table. Based on the case type I pickup a class name, a method name  from the database into respective strings and form an Argument String . How do I run this method dynamically? Please see sample code and suggest.

Thank You.
Normally if it weren't in the database I would run like this:
 ArgumentString = "/CaseNum " + caseNo + " /Ver " + StrVer + " /TemplateID " + TemplateID + " /ReportType " + ReportType;
Name189TFConsole.TF189(ArgumentString);

Open in new window


With the strings coming in strings from the database I have the following code:
ArgumentString = "/CaseNum " + caseNo + " /Ver " + StrVer + " /TemplateID " + TemplateID + " /ReportType " + ReportType;
                string ClassName= "Name189TFConsole";
                string MethodName = "TF189";
//code to run the Method here.

Open in new window


Thank You.
0
patd1
Asked:
patd1
  • 5
  • 5
1 Solution
 
Todd GerbertIT ConsultantCommented:
Use Type.GetType to initialize a Type to the appropriate class, and Type.InvokeMember to execute a method.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

class Program
{
	static void Main(string[] args)
	{
		string class1 = "TestClass1", method1 = "TestMethod", class2 = "TestClass2", method2 = "OtherTestMethod";

		Type type1 = Type.GetType(class1);
		type1.InvokeMember(method1, BindingFlags.InvokeMethod, null, type1, new object[] { });

		Type type2 = Type.GetType(class2);
		type2.InvokeMember(method2, BindingFlags.InvokeMethod, null, type2, new object[] { });

		Console.ReadKey();
	}
}

public class TestClass1
{
	public static void TestMethod()
	{
		Console.WriteLine("Hello World");
	}
}

public class TestClass2
{
	public static void OtherTestMethod()
	{
		Console.WriteLine("Other Method");
	}
}

Open in new window

0
 
Daniel Van Der WerkenIndependent ConsultantCommented:
Here is one way to do it.  It might not be the best or most efficient way, but it will work:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main( string[] args )
        {
            string className = "Name189TFConsole";
            string methodName = "TF189";

            Assembly thisAssembly = System.Reflection.Assembly.GetExecutingAssembly();
            Type[] thisTypes = thisAssembly.GetTypes();

            foreach ( Type t in thisTypes )
            {
                if ( string.Equals( t.Name, className, StringComparison.InvariantCultureIgnoreCase ) )
                {
                    MethodInfo[] methods = t.GetMethods();

                    foreach ( MethodInfo m in methods )
                    {
                        if ( string.Equals( m.Name, methodName, StringComparison.InvariantCultureIgnoreCase ) )
                        {
                            m.Invoke( null, null );
                        }
                    }
                }
            }
        }
    }

    public class Name189TFConsole
    {
        public Name189TFConsole() { }

        public static void TF189()
        {
            Console.WriteLine( "TF189" );
        }
    }
}

Open in new window

0
 
Todd GerbertIT ConsultantCommented:
I suppose Dan7el raises a good point about checking the stuff actually exists. ;)

Type.GetType will get the type for you without needing to enumerate all the types in the assembly, and will ignore case.  This example has a method to retrieve the corresponding MethodInfo from the Type (which is just about the same he has above), and then Invokes that MethodInfo.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

class Program
{
	static void Main(string[] args)
	{
		string class1 = "testclass1", method1 = "testmethod", class2 = "testclass2", method2 = "othertestmethod";


		Type t1 = Type.GetType(class1, false, true);
		if (t1 == null)
			Console.WriteLine("Could not find type {0}.", class1);
		else
		{
			MethodInfo staticMethod = GetStaticMethod(t1, method1);
			if (staticMethod == null)
				Console.WriteLine("Could not find method {0}.", method1);
			else
				staticMethod.Invoke(t1, new object[] { });
		}

		Console.ReadKey();
	}

	static MethodInfo GetStaticMethod(Type t, string MethodName)
	{
		foreach (MethodInfo mi in t.GetMethods())
		{
			if (mi.Name.Equals(MethodName, StringComparison.InvariantCultureIgnoreCase) && mi.Attributes.HasFlag(MethodAttributes.Static))
				return mi;
		}

		return null;
	}
}

public class TestClass1
{
	public static void TestMethod()
	{
		Console.WriteLine("Hello World");
	}
}

public class TestClass2
{
	public static void OtherTestMethod()
	{
		Console.WriteLine("Other Method");
	}
}

Open in new window

0
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

 
patd1Author Commented:
Thanks for the solution. I tried the one suggested by tgerbert and I get the following compilation error:

'System.Reflection.MethodAttributes' does not contain a definition for 'HasFlag' and no extension method 'HasFlag' accepting a first argument of type 'System.Reflection.MethodAttributes' could be found (are you missing a using directive or an assembly reference?)      

Any help is highly appreciated.

Thanks You
0
 
Todd GerbertIT ConsultantCommented:
HasFlag isn't available until .Net 4.0, you need to manually check the existence of a flag yourself otherwise.  Bit-wise "AND" the Attributes with MethodAttributes.Whatever - if MethodAttributes.Whatever is set the result will be non-zero.

if (aVariable & SomeEnum.SomeFlag != 0)
  hasFlag = true
else
  hasFlag = false

Open in new window

0
 
patd1Author Commented:
Thanks again.
How do I pass parameters to the method?
And how would the invoke change if the method is not static?

example:
76MapToORU Object76 = new 76MapToORU();
Object76.Run(caseNo, Ver, TemplateID, Status); 

Open in new window

0
 
Todd GerbertIT ConsultantCommented:
Passing parameters is done by passing an array object[] in the call t Invoke. In the snippet I posted above (http:#a35192673) on line 23 the call to Invoke has an empty object[] array because the method doesn't take parameters, if it did take a string and an int as parameters that line would have looked like:
staticMethod.Invoke(t1, new object[] { "Hello World", 33 });

Open in new window


A method that is not static is an instance method, and requires that a specific instance of a class (i.e. an object) be created before you can call it. For example, you can't write:
object littleLetters = String.ToLower();

Open in new window

Because it's senseless to say lower-case the string, without saying which string to lower-case. You need to create an instance of a string, then you can call .ToLower() on that instance:
string message = "HeLlO wOrLd";
object littleLetters = message.ToLower();

Open in new window


So, when you're doing it dynamically at run-time you can use Assembly.CreateInstance to create a specific instance of a class by specifying it's name as a string. http://msdn.microsoft.com/en-us/library/system.reflection.assembly.createinstance(v=VS.90).aspx

You can run this code below, and when prompted enter "TestClass" for a class name, and "Add" for a method name.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

class Program
{
	static void Main(string[] args)
	{
		Console.Write("Enter a class name to create instances of: ");
		string className = Console.ReadLine();

		Console.Write("Enter a method of the class to invoke: ");
		string methodName = Console.ReadLine();

		Type t = Type.GetType(className, false, true); // Get a Type representing the class we want to create an instance of

		if (t == null)
			Console.WriteLine("Could not find the class {0}.", className);
		else
		{
			// Create the instances of TestClass
			// The following CreateInstance call is equivelant to: object obj1 = new TestClass(10);
			object obj1 = t.Assembly.CreateInstance(
				className,					// Name of class to create instance of
				true,						// True to ignore case
				BindingFlags.CreateInstance,// Tell Reflection we're creating an instance of a class
				null,						// Use the default binder
				new object[] { 10 },		// Pass arguments to the constructor, in this case a single int
				null,						// Use current thread's culture info
				new object[] { });			// No activation attributes, so pass empty object[] array

			// This CreateInstance is equal to: object obj2 = new TestClass(50);
			object obj2 = t.Assembly.CreateInstance(className, true, BindingFlags.CreateInstance, null,
				new object[] { 50 }, null, new object[] { });

			if (obj1 == null || obj2 == null)
				Console.WriteLine("Could not create an instance of {0}.", className);
			else
			{
				MethodInfo method = GetPublicInstanceMethod(t, methodName);
				if (method == null)
					Console.WriteLine("Couldn't find the method {0} in the class {1}.", methodName, className);
				else
				{
					// Invoke the method - specify WHICH object to invoke the method on
					// in the first parameter to Invoke, and pass the parameters of the
					// method we're invoking as an array[] of objects
					object result1 = method.Invoke(obj1, new object[] { 5 });
					object result2 = method.Invoke(obj2, new object[] { 75 });

					Console.WriteLine("The {0} method of obj1 returned {1}.", methodName, result1);
					Console.WriteLine("The {0} method of obj2 returned {1}.", methodName, result2);

					// Get another test method with some more parameters
					MethodInfo m = GetPublicInstanceMethod(t, "GetMessage");
					// Pass all the parameters for the GetMessage method
					// in the object[] array
					object result3 = m.Invoke(obj1, new object[] { DateTime.Now, "Hello", "World", 17 });
					Console.WriteLine("Third result: {0}", result3);
				}
			}
		}

		Console.WriteLine("Press any key to exit.");
		Console.ReadKey();
	}

	static MethodInfo GetPublicInstanceMethod(Type t, string MethodName)
	{
		MethodName = MethodName.ToLower();
		foreach (MethodInfo mi in t.GetMethods())
		{
			// We're looking for a method with the specified name that is public, and is NOT static
			if (mi.Name.ToLower() == MethodName && HasMethodAttribute(mi, MethodAttributes.Public) && !HasMethodAttribute(mi, MethodAttributes.Static))
				return mi;
		}
		return null;
	}

	static bool HasMethodAttribute(MethodInfo method, MethodAttributes attribute)
	{
		return (method.Attributes & attribute) != 0;
	}
}

public class TestClass
{
	private int _value;
	public TestClass(int Value)
	{
		_value = Value;
	}
	public int Add(int ValueToAdd)
	{
		return _value + ValueToAdd;
	}
	public string GetMessage(DateTime dt, string part1, string part2, int n)
	{
		return String.Format("{0} {1} {2}, number: {3}",
			dt, part1, part2, n);
	}
}

Open in new window

0
 
patd1Author Commented:
I am getting this exception on line: Type t = Type.GetType(className, true, true);

base {System.SystemException} = {"Could not load type '76CGMapToORU ' from assembly 'Result_Generator_1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.":"76CGMapToORU "}

My current code:
 string className = "76CGMapToORU ";
 string methodName = "Run";
 Type t = Type.GetType(className, true, true); //I hv set throw on error to true to see the exception
....

Open in new window


If I run the code below, it runs fine.
 76CGMapToORU 76CGMapToORUObject = new 76CGMapToORU ();
 76CGMapToORUObject .Run(caseNo, Ver, TemplateID, ReportType);

Open in new window


Thanks for your help.
0
 
patd1Author Commented:
please read as: string className = "76CGMapToORU";
There is no extra space there in the code, but I still get the exception.
0
 
patd1Author Commented:
You can ignore the above messages. I appended the class name with namespace name and that worked.

Thanks for all your help.
0
 
Todd GerbertIT ConsultantCommented:
Type.GetType() assumes the current assembly.  If 76CGMapToORU appears in a separate assembly, will need to supply a fully-qualified type name (e.g. ClassLibrary1.Class1, ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, or get a reference to that assembly and use Assembly.GetType().
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

  • 5
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now