Solved

Running a static method dynamically

Posted on 2011-03-22
11
397 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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 20

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
Instantly Create Instructional Tutorials

Contextual Guidance at the moment of need helps your employees adopt to new software or processes instantly. Boost knowledge retention and employee engagement step-by-step with one easy solution.

 

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
 

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

Salesforce Made Easy to Use

On-screen guidance at the moment of need enables you & your employees to focus on the core, you can now boost your adoption rates swiftly and simply with one easy tool.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction Hi all and welcome to my first article on Experts Exchange. A while ago, someone asked me if i could do some tutorials on object oriented programming. I decided to do them on C#. Now you may ask me, why's that? Well, one of the re…
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…
Monitoring a network: why having a policy is the best policy? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the enormous benefits of having a policy-based approach when monitoring medium and large networks. Software utilized in this v…
Add bar graphs to Access queries using Unicode block characters. Graphs appear on every record in the color you want. Give life to numbers. Hopes this gives you ideas on visualizing your data in new ways ~ Create a calculated field in a query: …

615 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