Solved

Running a static method dynamically

Posted on 2011-03-22
11
354 Views
Last Modified: 2012-08-14
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
Comment
Question by:patd1
  • 5
  • 5
11 Comments
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 35192486
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
 
LVL 19

Expert Comment

by:Daniel Van Der Werken
ID: 35192535
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
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 35192673
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
 

Author Comment

by:patd1
ID: 35300417
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
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 35300440
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
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

Author Comment

by:patd1
ID: 35301254
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
 
LVL 33

Accepted Solution

by:
Todd Gerbert earned 500 total points
ID: 35304661
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
 

Author Comment

by:patd1
ID: 35315935
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
 

Author Comment

by:patd1
ID: 35315958
please read as: string className = "76CGMapToORU";
There is no extra space there in the code, but I still get the exception.
0
 

Author Comment

by:patd1
ID: 35316167
You can ignore the above messages. I appended the class name with namespace name and that worked.

Thanks for all your help.
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 35316171
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

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

Article by: Najam
Having new technologies does not mean they will completely replace old components.  Recently I had to create WCF that will be called by VB6 component.  Here I will describe what steps one should follow while doing so, please feel free to post any qu…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now