Generics came on the scene in .NET 2.0 and have been a mainstay ever since! I have developed numerous application frameworks over the years and in every case I have made full use of generics. Very similar to templates in C++, generics allow dynamic type-based operations in a strongly-typed manner and can provide build-time validation. Generics allow us define classes and methods without committing to specific types while we are coding. I am not going to go into a discussion of generics in a general sense since this MSDN resource does a pretty good job of providing a description.
Generic Methods
For our example on to how invoke generic methods via Reflection, we are first going to create a few simple classes whose actual function is a bit nonsensical, but I believe the best way to explain intermediate to advanced concepts is to use simple examples, and thus this is the theme of a large part of my blog :).
To start, using Visual Studio let’s create a new solution that we will call GenericMethodWalkthroughSolution.
Next, let’s add two projects to our solution: a C# Class Library which we will call MyClassLibrary and a C# Console Application which we will call MyConsoleApplication.
In the Console application, add a project reference to MyClassLibrary.
Right-click the MyConsoleApplication project and selected “Set as Startup Project”.
When we are done with these four steps, our Solution Explorer should look like this:
Solution Explorer View with both projects and a project reference added
Next, we will add four classes to our Class Library project. For fun, I am using classes that you may remember from your very first introduction to Object-Oriented Programming (OOP). Don’t laugh though as they are perfect for this discussion!
The classes are outlined below:
Mammal.cs – a class that will be the base class for the remaining two classes.
Human.cs – a class that will be instantiated to represent a human.
Dog.cs – a class that will be instantiated to represent a dog
Manager.cs – a class that will contain a generic method that will create an instance of a human or a dog, generically and a couple of methods that create either specifically.
We will just add a couple of properties to each of the first three then a single generic method to the fourth.
Since the Mammal class is our base class, let’s define a couple of properties that are common to all mammals. How about Weight and Age? Sounds good to me.
Our Mammal class looks like this:
public class Mammal
{
public Int32 Weight
{
get;
set;
}
public Int32 Age
{
get;
set;
}
}
Our Human class has a couple of properties and inherits Mammal:
public class Human : Mammal
{
public string Name
{
get;
set;
}
public String Occupation
{
get;
set;
}
}
Our Dog class has a couple of properties and inherits Mammal as well:
public class Dog : Mammal
{
public String Breed
{
get;
set;
}
public Boolean IsLongHaired
{
get;
set;
}
}
Finally, our Manager class has a single generic method called Create that is generic and accepts any type that inherits Mammal. See the simple code below:
public class Manager
{
public T CreateMammal<T>()
where T : Mammal, new()
{
return new T();
}
public Human CreateHuman()
{
return new Human();
}
public Dog CreateDog()
{
return new Dog();
}
}
These methods are quite simple, so let’s not focus on their simplicity but focus instead on the concept we’re discussing. The CreateHuman() and CreateDog() methods simply return a new instance of each type. The Create method does the same, but generically and we will move ahead with our examples of how to call generic methods via Reflection.
Let’s return to our Console Application’s Program class and write some code in our static Main() method as shown below.
static void Main(string[] args)
{
//first, let's just call the methods directly
Manager manager = new Manager();
//create a Human
Human firstHuman = manager.CreateHuman();
//create a Dog
Dog firstDog = manager.CreateDog();
//create a human generically
Human secondHuman = manager.CreateMammal<Human>();
//create a dog generically
Dog secondDog = manager.CreateMammal<Dog>();
}
I included this initial code simply to illustrate normal invocation of each of the methods. Now we are going to invoke each of the methods via Reflection in the code block below.
static void Main(string[] args)
{
Manager manager = new Manager();
Type managerType = manager.GetType();
//invoke the CreateHuman method
Human human = (Human)managerType.GetMethod("CreateHuman").Invoke(manager, null);
//invoke the CreateDog method
Dog dog = (Dog)managerType.GetMethod("CreateDog").Invoke(manager, null);
//invoke the CreateMammal method and create a Human
MethodInfo createMammalMethod = managerType.GetMethod("CreateMammal");
MethodInfo genericCreateMammalMethod = createMammalMethod.MakeGenericMethod(new Type[] { typeof(Human) });
Human genericHuman = (Human)genericCreateMammalMethod.Invoke(manager, null);
//invoke the CreateMammal method and create a Dog
MethodInfo createMammalMethodDog = managerType.GetMethod("CreateMammal");
MethodInfo genericCreateMammalMethodDog = createMammalMethodDog.MakeGenericMethod(new Type[] { typeof(Dog) });
Dog genericDog = (Dog)genericCreateMammalMethodDog.Invoke(manager, null);
}
Okay, the last generic method invocations require some discussion. Let’s break out the first generic invocation of the CreateMammal method in the code block below and insert some commentary to provide an explanation.
//invoke the CreateMammal method and create a Human
//As with the other non-generic, Reflection-based invocations in the first two
//methods, we first have to get a reference to the MethodInfo object for the CreateMammal method.
MethodInfo createMammalMethod = managerType.GetMethod("CreateMammal");
//When invoking a generic method, we have to call the MakeGenericMethod method to tell the runtime
//what generic arguments are involved. In this case, we have one argument whose type is Human.
//Therefore, we create a new MethodInfo object that contains the substituted types for the generic argument(s).
MethodInfo genericCreateMammalMethod = createMammalMethod.MakeGenericMethod(new Type[] { typeof(Human) });
//Instead of using the first MethodInfo object we actually invoke the second (generic) one.
Human genericHuman = (Human)genericCreateMammalMethod.Invoke(manager, null);
So the important point here is this: to invoke a generic method via Reflection, you must call the MakeGenericMethod method of the MethodInfo object that holds a reference to the method prior to substitution of the generic arguments. Remember that a generic method may have more than one generic type, so therefore the MakeGenericMethod method accepts an object array that is the collection of generic types.
Now, if we don’t know the generic types at runtime like we did in this example, we can get them through the same MethodInfo object! See the code block below:
If we place a breakpoint on the fourth line of this block and add a Watch to the genericArguments object we can see for ourselves that the generic type is a type that has a BaseType of Mammal. See below:
In a previous post, we took a brief look at the System.Reflection.Assembly class. In this post, we are going to move down a level and take a look at the Type class. Every object that resides within a .NET application is a specific Type.
Getting an Object’s Type (Type.GetType() method)
Let’s go ahead and dive in to some code samples that allow us to work with types a little. In the previous post we created a very simple Visual Studio solution that contained a C# Class Library and a C# Console Application. In this post we will add a new class to the console application then from the Program class’s static Main() method we will instantiate our new object. We will work with our new class and add methods to perform certain tasks related to getting Type information.
We will add a new class to the console application and we will name it ‘Person’. Since a person has qualities such as a name, age, height, weight, and so on, we will add a few properties to the class that we will then retrieve and work with via Reflection.
So when we add a few properties and a couple of nonsensical methods to the class, the full code of the Person class looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyConsoleApplication
{
public class Person
{
#region Public Properties
public string Name
{
get;
set;
}
public Int32 Age
{
get;
set;
}
public Int32 Height
{
get;
set;
}
public Int32 Weight
{
get;
set;
}
#endregion Public Properties
#region Public Methods
public string SayHello()
{
return "Hello!";
}
public string Speak(string wordsToSpeak)
{
return wordsToSpeak;
}
#endregion Public Methods
}
}
To create an instance of a Person, we add the following code to our console application’s Program class’s Main() method:
Person person = new Person();
Type personType = person.GetType();
Now, we will put a breakpoint on the second line shown above and hit F5 to run the console application. When we hit the breakpoint, we will right-click and set a Watch on the personType object. The results are shown below:
Watch window view of the personType object
There are more properties of the Type object than I can display in the illustration above but this is more than enough to get us started. If you scan through the property list, you will that there is a lot of information about the type available.
Let’s talk about a few of the more important properties shown.
First, look at the Assembly property. You can see that the value describes the ConsoleApplication assembly, including its version, culture information, public key token (which is null because the assembly is not strongly-named).
Next, the AssemblyQualifiedName shows the same stuff, except the full namespace and type name of the Person object are prepended.
The BaseType property describes the type from which the Person class inherits. In this case, it inherits from System.Object. Had our class inherited another class, the BaseType would reflect that type, and its BaseType would reflect its base type and so on. Ultimately, the lowest level base class would have a BaseType of System.Object. Make sense?
All if the properties that start with Is* are Boolean values that indicate what their names describe.
So far so good? Great! Let’s move on to some things that are a little more interesting.
Getting the Properties within a Type (Type.GetProperties() method)
The Type class’s GetProperties() method allows us to retrieve all properties within a type. This method returns an array of PropertyInfo objects. The PropertyInfo type provides us access to the metadata of a property. Sound confusing? Let’s just dive into some code and clear it up!
Let’s return to our Main() method and add the following code:
Person person = new Person();
Type personType = person.GetType();
//get the PropertyInfo array for all properties
PropertyInfo[] properties = personType.GetProperties();
//create a StringBuilder to hold our formatted output
StringBuilder sb = new StringBuilder();
//iterate the properties and write output
foreach (PropertyInfo property in properties)
{
sb.AppendLine("================================================================");
sb.AppendLine(String.Format("Property Name: {0}", property.Name));
sb.AppendLine("================================================================");
sb.AppendLine(String.Format("Property Type Name: {0}", property.PropertyType.Name));
sb.AppendLine(String.Format("Property Type FullName: {0}", property.PropertyType.FullName));
sb.AppendLine(String.Format("Can we read the property?: {0}", property.CanRead.ToString()));
sb.AppendLine(String.Format("Can we write the property?: {0}", property.CanWrite.ToString()));
}
string output = sb.ToString();
When we retrieve the string contents of our StringBuilder, we will see the following results:
================================================================ Property Name: Name ================================================================ Property Type Name: String Property Type FullName: System.String Can we read the property?: True Can we write the property?: True ================================================================ Property Name: Age ================================================================ Property Type Name: Int32 Property Type FullName: System.Int32 Can we read the property?: True Can we write the property?: True ================================================================ Property Name: Height ================================================================ Property Type Name: Int32 Property Type FullName: System.Int32 Can we read the property?: True Can we write the property?: True ================================================================ Property Name: Weight ================================================================ Property Type Name: Int32 Property Type FullName: System.Int32 Can we read the property?: True Can we write the property?: True
All of the properties that defined in our Person class are shown here with their data types (both short and full names) and with boolean values that indicate whether they are read-only or write-only properties. The presence of get and set accessors means that our properties are read and write properties. Pretty simple.
Getting and Setting Properties through Reflection
Being able to retrieve our properties is pretty cool, but we can also read values from and write values to our properties using Reflection. Now if we did just simply through the object itself, we could do it very simply like this:
Okay, this warrants some discussion. To set property values via Reflection, you must use the Type.GetProperty() method, then invoke the PropertyInfo.SetValue() method. The default overload that we used accepts the object in which to set the property value, the value itself, and an object array, which in our example is null. The MSDN resource here explains the overloads of the SetValue() method in more detail.
Now that we’ve set the values using Reflection, let’s get them in the same way. We would do that as shown below:
string name = personType.GetProperty("Name").GetValue(person).ToString();
int age = (int)personType.GetProperty("Age").GetValue(person);
int weight = (int)personType.GetProperty("Weight").GetValue(person);
int height = (int)personType.GetProperty("Height").GetValue(person);
Getting property values is pretty straightforward and easy using Reflection.
Getting the Methods within a Type (Type.GetMethods() method)
In a similar manner to getting the properties within a type, getting and working with methods is pretty simple. For this sample, our methods do not contain generic arguments and I did this to keep the sample simple. We will cover invoking generically-typed methods in a later post.
The following code gets all the methods within our Person type and writes information about each method to a StringBuilder.
Person person = new Person();
Type personType = person.GetType();
//get the methods in the type
MethodInfo[] methods = personType.GetMethods();
//create a StringBuilder to hold our formatted output
StringBuilder sb = new StringBuilder();
//iterate the methods and write output
foreach (MethodInfo method in methods)
{
sb.AppendLine("================================================================");
sb.AppendLine(String.Format("Method Name: {0}", method.Name));
sb.AppendLine("================================================================");
sb.AppendLine(String.Format("Contains Generic Parameters: {0}", method.ContainsGenericParameters.ToString()));
sb.AppendLine(String.Format("Is Abstract?: {0}", method.IsAbstract.ToString()));
sb.AppendLine(String.Format("Is a Constructor?: {0}", method.IsConstructor.ToString()));
sb.AppendLine(String.Format("Is it Generic?: {0}", method.IsGenericMethod.ToString()));
sb.AppendLine(String.Format("Is it Private?: {0}", method.IsPrivate.ToString()));
sb.AppendLine(String.Format("Is it Public?: {0}", method.IsPublic.ToString()));
sb.AppendLine(String.Format("Is it Static?: {0}", method.IsStatic.ToString()));
sb.AppendLine(String.Format("Is is Virtual?: {0}", method.IsVirtual.ToString()));
//if the method is a void, the Return type will be null
//otherwise, it will return a Type
if (method.ReturnType != null && !String.IsNullOrEmpty(method.ReturnType.Name))
{
sb.AppendLine(String.Format("Return Type: {0}", method.ReturnType.Name.ToString()));
}
//there are more properties of the MethodInfo you could output...
}
string output = sb.ToString();
If we execute this code and retrieve the results of the StringBuilder.ToString() method, we will see the following output:
================================================================
Method Name: get_Name
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type?: String
================================================================
Method Name: set_Name
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Void
================================================================
Method Name: get_Age
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Int32
================================================================
Method Name: set_Age
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Void
================================================================
Method Name: get_Height
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Int32
================================================================
Method Name: set_Height
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Void
================================================================
Method Name: get_Weight
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Int32
================================================================
Method Name: set_Weight
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Void
================================================================
Method Name: SayHello
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: String
================================================================
Method Name: Speak
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: String
================================================================
Method Name: ToString
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: True
Return Type: String
================================================================
Method Name: Equals
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: True
Return Type: Boolean
================================================================
Method Name: GetHashCode
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: True
Return Type?: Int32
================================================================
Method Name: GetType
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Return Type: Type
This is odd because we only defined two methods for our class, right? Well, it actually makes perfect sense because we retrieve ALL of the methods for the Person Type.
Beginning from the top of the results list, take a look at the methods whose names begin with “get_” and “set_”. It just so happens that properties are implemented in MSIL as methods! The get accessor is actually a method with a name that begins with “get_” and the set accessor is a method that begins with “set_”. So in reality, each of our properties created two (2) methods.
The ToString(), Equals(), GetHashCode(), and GetType() methods are actually inherited from the System.Object class which is the default base class of ALL objects in .NET.
So if we eliminate these methods, we are left with our two public methods, SayHello() and Speak().
Invoking a Method via Reflection
When dealing with non-generic methods, there are two basic ways to invoke them dynamically.
The first approach is to get a reference to the MethodInfo object for the method to be called, then calling the MethodInfo.Invoke(object, object[]) method. With this approach, the first argument is the object within which the method is to be called. In our case the ‘person’ object. The second argument is an object array that accepts the arguments expected by the method in the order they are specified in the method.
The second approach is to Type.InvokeMenber(methodName, Binder, object, object[]) method overload. I chose this overload of the InvokeMember method because it is the simplest and is perfect for illustrative purposes.
So let’s get going with some code to test this out! Let’s stick with our iterative approach to loop through the methods as we did above, but lets add some code that checks to see if the method name is “Speak” or “SayHello”, then invoke the method using Approach #1 first.
If we replace the code with the code listed below (see new lines 32-44):
//get the methods in the type
MethodInfo[] methods = personType.GetMethods();
//create a StringBuilder to hold our formatted output
StringBuilder sb = new StringBuilder();
//iterate the methods and write output
foreach (MethodInfo method in methods)
{
sb.AppendLine("================================================================");
sb.AppendLine(String.Format("Method Name: {0}", method.Name));
sb.AppendLine("================================================================");
sb.AppendLine(String.Format("Contains Generic Parameters: {0}", method.ContainsGenericParameters.ToString()));
sb.AppendLine(String.Format("Is Abstract?: {0}", method.IsAbstract.ToString()));
sb.AppendLine(String.Format("Is a Constructor?: {0}", method.IsConstructor.ToString()));
sb.AppendLine(String.Format("Is it Generic?: {0}", method.IsGenericMethod.ToString()));
sb.AppendLine(String.Format("Is it Private?: {0}", method.IsPrivate.ToString()));
sb.AppendLine(String.Format("Is it Public?: {0}", method.IsPublic.ToString()));
sb.AppendLine(String.Format("Is it Static?: {0}", method.IsStatic.ToString()));
sb.AppendLine(String.Format("Is it Virtual?: {0}", method.IsVirtual.ToString()));
//if the method is a void, the Return type will be null
//otherwise, it will return a Type
if (method.ReturnType != null && !String.IsNullOrEmpty(method.ReturnType.Name))
{
sb.AppendLine(String.Format("Is is Private?: {0}", method.ReturnType.Name.ToString()));
}
if (method.Name == "SayHello")
{
object result = method.Invoke(person, new object[] { });
if (result != null)
{
sb.AppendLine(String.Format("***Calling the SayHello() method. The result of the method call is: {0}", result.ToString()));
}
}
else if (method.Name == "Speak")
{
object result = method.Invoke(person, new object[] { "How are you today?" });
sb.AppendLine(String.Format("***Calling the Speak() method. The result of the method call is: {0}", result.ToString()));
}
//there are more properties of the MethodInfo you could output...
}
string output = sb.ToString();
When we execute the new code the output for the Speak() and SayHello() methods now includes the results too:
================================================================
Method Name: SayHello
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Is it Private?: String ***Calling the SayHello() method. The result of the method call is: Hello!
================================================================
Method Name: Speak
================================================================
Contains Generic Parameters: False
Is Abstract?: False
Is a Constructor?: False
Is it Generic?: False
Is it Private?: False
Is it Public?: True
Is it Static?: False
Is it Virtual?: False
Is it Private?: String ***Calling the Speak() method. The result of the method call is: How are you today?
Pay attention to the lines in blue. We actually called those methods and added the results in a formatted way to our sample output StringBuilder. Pretty simple, right?
Now we take a slightly different approach and simply invoke the method via Reflection by name. This is very straightforward and can be done as shown below:
//invoke the SayHello method
object methodResult = personType.GetMethod("SayHello").Invoke(person, null);
if (methodResult != null)
{
string thePersonSaid = methodResult.ToString();
}
//invoke the Speak method and pass the string argument in
object methodResult2 = personType.GetMethod("Speak").Invoke(person, new object[] { "I have spoken." });
if (methodResult2 != null)
{
string thePersonThenSaid = methodResult2.ToString();
}
So now we have looked at a couple of ways to invoke methods via Reflection. One method accepts no arguments and the other accepted a string. What if we had a method that accepted two or more arguments?
Go back to our Person class and add the following method:
public string CallTheChildrenInForDinner(string firstChild, string secondChild)
{
return String.Format("{0}, {1} it is time for dinner!", firstChild, secondChild);
}
We can invoke this method with the following code:
object methodResult3 = personType.GetMethod("CallTheChildrenInForDinner").Invoke(person, new object[] { "Bobby", "Gene" });
if (methodResult3 != null)
{
string dinnerCall = methodResult3.ToString();
}
The result of this method call is: Bobby, Gene it is time for dinner!.
Getting the Interfaces Implemented by a Type
Within C#, a class can only inherit one base class (i.e. can only have one BaseType). A class can, however implement multiple interfaces. An interface looks much like a class, but only contains the definitions (signatures) of the public properties, methods, events, and indexers within the class that implements it. When discussing interfaces, we use the term “implements” for any class whose construction is governed by an interface. It is an accepted rule that interface names should generally begin with the letter “I”.
The Type class contains methods that allow us to retrieve interface implementation details for any given type. To take a look at this, let’s add an IPerson interface to our class library project. The code below is the full definition of the IPerson interface:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyConsoleApplication
{
public interface IPerson
{
#region Public Properties
string Name { get; set; }
Int32 Age { get; set; }
Int32 Height {get; set; }
Int32 Weight { get; set; }
#endregion Public Properties
#region Public Methods
string SayHello();
string Speak(string wordsToSpeak);
string CallTheChildrenInForDinner(string firstChild, string secondChild);
#endregion Public Methods
}
}
Now that we’ve created our interface, let’s flip over to our Person class and implement the interface.
public class Person : IPerson
Now that we’ve done this, let’s modify the code in our Main() method to find the interfaces implemented by the Person type. See the code below:
Person person = new Person();
Type personType = person.GetType();
Type[] interfaces = personType.GetInterfaces();
If we place a breakpoint on the last line of the code above and add a Watch to the interfaces object, we will see the output displayed below:
Watch output for personType.GetInterfaces() method
This shows us that the IPerson interface is implemented by the Person type. If we expand the watch definition for the IPerson type we see all the properties for that that we saw for the Person class. The differences will be in the IsClass and IsInterface properties. Pretty simple.