Create code at runtime with Reflection in .NET C#: Methods

The previous post in this miniseries got us as far as defining a default and an overloaded constructor for our custom type:

ConstructorBuilder defaultConstructorBuilder = simpleType.DefineDefaultConstructor(MethodAttributes.Public);
ConstructorBuilder constructorBuilder = simpleType.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(string) });

We can add methods to the type in the following ways. Here’s how to create a void method called Calculate which accepts two integers as parameters:

MethodBuilder calculateFunctionBuilder = simpleType.DefineMethod("Calculate", MethodAttributes.Public, null, new Type[] { typeof(int), typeof(int) });

Note the ‘null’ parameter which defines that there’s no return type. Consequently here’s how to add a return type of int:

MethodBuilder calculateFunctionBuilder = simpleType.DefineMethod("Calculate", MethodAttributes.Public, typeof(int), new Type[] { typeof(int), typeof(int) });

If there are no parameters to the method then leave the last item as null:

MethodBuilder calculateFunctionBuilder = simpleType.DefineMethod("Calculate", MethodAttributes.Public, typeof(int), null);

…and here’s how to define a static method:

MethodBuilder calculateFunctionBuilder = simpleType.DefineMethod("Calculate", MethodAttributes.Public | MethodAttributes.Static, typeof(int), null);

We’ll of course need a method body as well and just like we saw in the case of constructors this operation can be quite complex. Check the post on creating constructors programmatically for my notes and links regarding the .NET intermediate language instructions, the ILGenerator and OpCodes objects for further information.

View all posts on Reflection here.

Advertisement

Dynamically invoking a static method with Reflection in .NET C#

Say you do not have access to a .NET assembly at compile time but you want to run code in it. It’s possible to dynamically load an assembly and run code in it without early access.

Here we’ll see how to invoke a static method of a type in a referenced assembly. It is very similar to how you would invoke an instance-level method. Check out the following post for related topics:

Open Visual Studio 2012/2013 and create a new C# class library project called Domain. Add the following Customer class to it:

public class Customer
{
	private string _name;

	public Customer() : this("N/A")
	{}

	public Customer(string name)
	{
		_name = name;
	}

        public static int CallStaticMethod(int inputOne, int inputTwo)
	{
		return inputOne + inputTwo;
	}
}

Build the solution and locate the compiled Domain.dll library. It should be located in either the Debug or Release folder within the bin folder depending on the compilation configuration in VS. Copy the .dll and put it somewhere else on your main drive where you can easily find it. We’re pretending that you got the library from another source but you for whatever reason cannot reference it at compile time. E.g. the source is loaded into your app as a plugin which follows some naming conventions so that your code can unwrap it and invoke its code.

Let’s see how we can dynamically call the CallStaticMethod method and read its result:

string pathToDomain = @"C:\Studies\Reflection\Domain.dll";
Assembly domainAssembly = Assembly.LoadFrom(pathToDomain);
Type customerType = domainAssembly.GetType("Domain.Customer");
MethodInfo staticMethodInfo = customerType.GetMethod("CallStaticMethod");
int returnValue = Convert.ToInt32(staticMethodInfo.Invoke(null, new object[] { 3,5 }));

You should obviously adjust the path to Domain.dll.

The code to call a static method is almost the same as calling an instance-level one. The key difference is that we pass in null as the first parameter to Invoke. That parameter specifies which instance the method should be invoked on. As there’s no instance here, we can skip the step of first invoking the constructor of Customer.

‘returnValue’ will be 8 as expected.

View all posts on Reflection here.

Dynamically invoking a method with Reflection in .NET C#

Say you do not have access to a .NET assembly at compile time but you want to run code in it. It’s possible to dynamically load an assembly and run code in it without early access.

Here we’ll see how to invoke a method of a type in a referenced assembly.

Open Visual Studio 2012/2013 and create a new C# class library project called Domain. Add the following Customer class to it:

public class Customer
{
	private string _name;

	public Customer() : this("N/A")
	{}

	public Customer(string name)
	{
		_name = name;
	}

        public void DoVoidMethod(int intParameter, string stringParameter)
	{
		Console.WriteLine("Within Customer.DoVoidMethod. Parameters: {0}, {1}", intParameter, stringParameter);
	}

	public int DoRetMethod(int intParameter)
	{
		return intParameter + 1;
	}
}

Build the solution and locate the compiled Domain.dll library. It should be located in either the Debug or Release folder within the bin folder depending on the compilation configuration in VS. Copy the .dll and put it somewhere else on your main drive where you can easily find it. We’re pretending that you got the library from another source but you for whatever reason cannot reference it at compile time. E.g. the source is loaded into your app as a plugin which follows some naming conventions so that your code can unwrap it and invoke its code.

In this post we saw how to invoke a constructor so we won’t go into that again. Once you have an instance of the object then you can use the Type object to find the available methods, properties, events etc. of that type: MethodInfo, EventInfo, PropertyInfo etc.

Let’s see how we can get hold of the void method. First we’ll invoke the overloaded constructor:

string pathToDomain = @"C:\Studies\Reflection\Domain.dll";
Assembly domainAssembly = Assembly.LoadFrom(pathToDomain);
Type customerType = domainAssembly.GetType("Domain.Customer");
Type[] stringArgumentTypes = new Type[] { typeof(string) };
ConstructorInfo stringConstructor = customerType.GetConstructor(stringArgumentTypes);
object newStringCustomer = stringConstructor.Invoke(new object[] { "Elvis" });

Then we locate the DoVoidMethod method and invoke it on the newStringCustomer object. We also provide an object array to represent the arguments to the method.

MethodInfo voidMethodInfo = customerType.GetMethod("DoVoidMethod");
voidMethodInfo.Invoke(newStringCustomer, new object[] { 3, "hello" });

If you run this code then a Console window should pop up with the message “Within Customer.DoVoidMethod. Parameters: 3, hello” on it.

Next we’ll invoke the DoRetMethod method and read its return value:

MethodInfo retMethodInfo = customerType.GetMethod("DoRetMethod");
int returnValue = Convert.ToInt32(retMethodInfo.Invoke(newStringCustomer, new object[] { 4 }));

The returnValue variable will be 5 as expected.

View all posts on Reflection here.

Examining the method body using Reflection in .NET C#

In this short post we saw how to extract the members of a class: constructors, properties, methods etc. Even more exciting is the fact that you can peek into the body of a method. Well, not the plain text C# or VB code, but the Intermediate Language – MSIL version of it.

The MethodBody object represents, as the name suggests, the body of a method including the local variables and the MSIL instructions. MethodBody is available on classes that derive from the MethodBase class, which are methods and constructors – MethodInfo and ConstructorInfo.

Consider the following Customer class:

public class Customer
{
	private string _name;

	public Customer(string name)
	{
		if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("Customer name!");
		_name = name;
	}

	public string Name
	{
		get
		{
			return _name;
		}
	}
	public string Address { get; set; }
	public int SomeValue { get; set; }

	public int ImportantCalculation()
	{
		int variable = 2;
		string stringVar = string.Empty;
		if (variable == 2)
		{
			stringVar = "two";
		}
		else
		{
			stringVar = "hello";
		}

		ImportantVoidMethod();
		return 1000;
	}

	public void ImportantVoidMethod()
	{
		bool ok = false;
		SomeEnumeration enumeration = SomeEnumeration.ValueOne;
		switch (enumeration)
		{
			case SomeEnumeration.ValueOne:
				ok = true;
				break;
			case SomeEnumeration.ValueTwo:
				ok = false;
				break;
			default:
				ok = false;
				break;
		}
	}

	public enum SomeEnumeration
	{
		ValueOne = 1
		, ValueTwo = 2
	}

	public class SomeNestedClass
	{
		private string _someString;
	}
}

The following code shows you how you can extract the methods and inspect them:

Type customerType = typeof(Customer);

Console.WriteLine("Customer methods: ");
MethodInfo[] methods = customerType.GetMethods();

foreach (MethodInfo mi in methods)
{
	Console.WriteLine(mi.Name);
	MethodBody methodBody = mi.GetMethodBody();
	if (methodBody != null)
	{
		byte[] ilCode = methodBody.GetILAsByteArray();
		int maxStackSize = methodBody.MaxStackSize;
		IList<LocalVariableInfo> localVariables = methodBody.LocalVariables;
		Console.WriteLine("Max stack size: {0}", maxStackSize);

		Console.WriteLine("Local variables if any:");
		foreach (LocalVariableInfo lvi in localVariables)
		{
			Console.WriteLine("Type: {0}, index: {1}.", lvi.LocalType, lvi.LocalIndex);
		}

		Console.WriteLine("IL code:");
		StringBuilder stringifiedIlCode = new StringBuilder();
		foreach (byte b in ilCode)
		{
			stringifiedIlCode.Append(string.Format("{0:x2} ", b));
		}

		Console.WriteLine(stringifiedIlCode);
	}
}

The MethodInfo array will include the properties that are turned into methods, e.g. Name will become get_Name, and also the methods inherited from Object such as ToString(). Here’s the output for ImportantVoidMethod and ImportantCalculation:

MethodBody example code output

The LocalVariableInfo doesn’t contain the name of the variable because the metadata about a type doesn’t keep the variable name, only its order.

View all posts on Reflection here.

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: