Ayende @ Rahien

Refunds available at head office

Static Reflection

I've a confession to make. It's a very serious one. I don't like strings and Reflection. In fact, you could say that I hate them with a passion. I hate them because they hide important information from the compiler. This post is actually not a rant, it presents a solution to the problem.

I hate them so much that I wrote my own Mocking Framework that don't use strings at all. .Net has some really great stuff that you can do with Reflection, but it has two big problems.

  • It is a performance hit.
  • It uses strings.

I refactor a lot, and I just hate it when everything compiles okay, and then things breaks on runtime. I think that static reflection will enable writing amazing code. The functionality is already built into the CLR (check the ldtoken/ldftn IL instructions and friends). It didn't make it into the 2.0 release, but I hope that it will be in the next release. In the meantime, the 2.0 Reflection has been optimized, so that is what we have for now. But it's not enough for me. I discovered that you can do this:

Action<int> act = this.CalcSums;

So I figured out that I can create a library that would cheat the C# compiler (and I belive the VB.Net compiler as well) to give me a token for the method. Before I'll get into the details, here is a short sample of the code:

using System;
using System.Reflection;
public class StaticReflection
{
        #region void delegates
        public delegate void VoidFunc();
        public delegate void VoidFunc<A0>(A0 a0);
        #endregion
        #region delegates
        public delegate TRet Func<TRet>();
        public delegate TRet Func<TRet, A0>(A0 a0);
        #endregion
        #region void func Method Info
        public static MethodInfo VoidMethodInfo(VoidFunc func0)
        {
                return func0.Method;
        }
        public static MethodInfo VoidMethodInfo<A0>(VoidFunc<A0> func1)
        {
                return func1.Method;
        }
        #endregion
        #region func Method Info
        public static MethodInfo MethodInfo<TRet>(Func<TRet> func0)
        {
                return func0.Method;
        }
        public static MethodInfo MethodInfo<TRet, A0>(Func<TRet, A0> func1)
        {
                return func1.Method;
        }

        #endregion
}

The usage is simple:

public class Test
{
 public static void Main(string[]args)
 {
  MethodInfo thisMethod = StaticReflection.MethodInfo<int,int>(MultiplyByThree);
  Console.WriteLine(thisMethod);
 }
 
 public static int MultiplyByThree(int i)
 {
  return i * 3;
 }
}

The output of the above code is: "Int32 MultiplyByThree(Int32)"

Now, there is one huge advantage of this system, and it's the simple fact that it will fail to compile if you changed the name of the method and not the calling code. The other advantage it should have is in performance.

In order to test that I created two sets of tests. The tests are two classes, each with 10,000 methods, each class Main() needs to get a MethodInfo object for each of the methods. Here is a sample of the code:

Benchmarking the static reflection:

public static void Main(string [] args)
{
 DateTime start = DateTime.Now;
 MethodInfo method;
 method = StaticReflection.VoidMethodInfo(DemoClass0.DemoMethod0);
// ... 9998 more statements ... 
 method = StaticReflection.VoidMethodInfo(DemoClass9999.DemoMethod9999);
 Console.WriteLine("Took: {0}", DateTime.Now - start);
}

Benchmarking normal Reflection:

public class TestingUsingReflection
{
 public static MethodInfo GetMethod(Type type, string method)
 {
  return type.GetMethod(method);
 }
 
 public static void Main(string [] args)
 {
  DateTime start = DateTime.Now;
  MethodInfo method;
  method = GetMethod(typeof(DemoClass0),"DemoMethod0");
// ... 9998 more statements ... 

  method = GetMethod(typeof(DemoClass9999),"DemoMethod9999");
  Console.WriteLine("Took: {0}", DateTime.Now - start);
 } 

Here are the results.

Reflection .Net 2.0 & 1.1  218 - 187 Milliseconds
Static Delegates 125 Milliseconds

It's not a huge difference in the number, but it's something. And it's for 10,000 iterations. I was actually surpirsed that this happened. There doesn't seem to be any difference between 1.1 & 2.0 in this case (I imagine that it's not one that they optimized.) Using the static delegates took exactly 125 milliseconds each time I run it. It's not the performance that I'm looking at as the biggest advantage, it's the type safety in compile time.

Some things to note, there are two methods on the Static Reflection class, one for methods that has a return type, and one for those who don't. You can read the reason for that in Representing void methods with generic delegates.

The code (including the scripts I've used for generating the class and the benchmarks) are here. (You'll need Boo in order to run the benchmarks)

Enjoy,

Comments

Xanax.
03/01/2007 12:15 PM by
Xanax.

Xanax.

Xenical.
03/11/2007 08:54 AM by
Xenical.

Xenical.

Lipitor.
03/16/2007 12:22 AM by
Lipitor.

Lipitor.

Ambien.
03/19/2007 05:41 PM by
Ambien.

Ambien.

Comments have been closed on this topic.