Saturday, December 13, 2008

C#, Generics, and Reflection

On my current project, we are using a homegrown ORM framework that works against stored procedures. The framework is fairly slick, uses generics, and does save time by producing basic plumbing as well as the stored procedures. As to why we are not using NHibernate and Spring.NET, the client does not have much faith in Open Source projects and only allows stored procedures to access their database. Though I have opinions about that, we will not share those here.

Since the application we are writing must coexist side by side with the legacy application and it’s other modules, we must also use the legacy tables. The application uses lots of lookup tables, which could really all be rolled up into a single table with types but I digress. We decided that instead of having several classes to do the CRUD for the lookup types, we will be writing a single class to manage all of lookup tables.

So my team member, Jes, created the stored procedure to let us return any of the lookup values based on the parameters that we pass in. The problem reared its ugly head when she tried to use the framework in association with a single common stored procedure. The framework expects the generic type parameter and we don’t have this value till runtime and there is not much that could be done here without the use of reflection.

Reflection allows an application to use introspection to to work with some piece of code and can be a very powerful tool in the bag o’ tricks. I have used reflection extensively in my days of java development but not much in the .NET world. While thinking about the problem, I started thinking about reflection once again and started playing with it. As it turns out, reflection is even easier in .NET than it was in java (which was really cool and easy).

After playing with it a little, I realized that you can check to see if a method is a generic method and whether it has a generic type parameter. Once I realized that, I knew our problem had been solved. Below is an example of a class that has a method with a type parameter. The code uses reflection to dynamically pass the parameter type. No, the code is not production quality and makes assumptions that should always be validated but that’s not the point here


using System;
using System;
namespace Test {
  public class Hello {
    public void PrintTypeName() {
      Console.WriteLine("Parameter Type Name: {0}", typeof(T).FullName);
    }
  }
  class Program {
    static void Main(string[] args) {
      // instantiate the object and store the type information...will need it again
      var instance = Activator.CreateInstance(Type.GetType("Test.Hello"));
      var type = instance.GetType();

      // lookup the method you want to work on.  This is just for demonstration purposes
      // only...there are better ways to lookup information
      var m = type.GetMethod("PrintTypeName");

      // this call makes the method into a generic method passing in the type parameter
      // information.  DO note that you must assign back the value returned from the
      // method call.  If you fail to do the assignment, you will get the following exception
      // which I did but didn't find much help on google and is the whole point of this blog
      // Exception: "Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true"
      m = m.MakeGenericMethod(type);

      // now that you have a generic method, all that is needed is the invocation.
      // Since there are no argument values for the method, we are passing in the null
      // Also note that we must have an instance to execute the method on since the method is not
      // static.  If the method was static, instance would be passed in as null as well
      m.Invoke(instance, null);

      // Sam Bradford of OU won the Heisman today [12.13.2008]
      // Boomer Sooner!
      Console.Write("Press any key...");
      Console.ReadKey();
    }
  }
}

2 comments:

Anonymous said...

Thank you very much for this post. It really helped me with this nasty exception!

John Currah said...

helpful post - thanks!