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,

Print | posted on Saturday, October 29, 2005 3:08 PM

Feedback


#  10/30/2005 2:30 PM AndrewSeven

Very nice idea.


Gravatar

#  4/17/2006 10:57 PM Marc Brooks

You know... people are still reinventing this and getting it wrong. Here's my latest trackback to here:

http://musingmarc.blogspot.com/2006/04/tale-of-two-implementations.html


Gravatar

#  7/5/2006 7:32 PM Mladen

Nice post.

I was wondering though... for each method that has N parameters you have to create a new delegate.
eg.:
public void Method1(string str)
public void Method2(string str, int num)
public void Method3(string str, Rectangle rect)

you'd have to create 3 delegates that match those parameters.

Is there a workaround or something else i'm not familiar with??


Gravatar

#  7/5/2006 10:31 PM Ayende Rahien

No, there isn't.
My lib has delegate up to 9 parameters.
If you go beyond it, you are on your own.


#  7/6/2006 6:43 PM Fabrice

Daniel Cazzulino has another approach. See http://weblogs.asp.net/cazzu/archive/2006/07/06/Linq-beyond-queries_3A00_-strong_2D00_typed-reflection_2100_.aspx


#  7/20/2006 10:58 PM Judah

As far ast the &quot;lots of delegates&quot; question, maybe I'm not understanding functional programming yet, but isn't this something currying is supposed to take care of? IIRC, currying can be done as of C# 2.

Ayende, I have a comment regarding mocks and string-based reflection. Here's how I ended up using NUnit mocks but with compile time support:

string GetMethodName(Function method)
{
return method.MethodInfo.Name;
}

...

mock.Expect(GetMethodName(theMethodExpected))

This way, every time NUnit mocks ask for the name of a function, I pass in GetMethodName(theMethod). This allows for the compiler to prevent runtime errors; if the name changes due to a refactor, the string-based reflection still works.

Unfortunately, this doesn't work with properties since one cannot use delegates on getter properties. Too bad, too.


Gravatar

#  7/20/2006 11:29 PM Ayende Rahien

@Judah, This will not work for method that has overloads.


# Common Odds and Ends 2/3/2007 7:49 AM www.ayende.com


# What I did on 2005 2/3/2007 7:49 AM www.ayende.com


# How to invoke a method asynchronously 2/3/2007 7:49 AM www.ayende.com


# How to invoke a method asynchronously 2/3/2007 7:49 AM blogs.microsoft.co.il


# Rhino Commons 2/3/2007 7:49 AM www.ayende.com


# Static method reflection 2/3/2007 7:49 AM weblogs.asp.net


# Strongly-typed reflection redux 2/3/2007 7:49 AM weblogs.asp.net


# Strongly-Typed Reflection 2/3/2007 7:49 AM www.dotnetkicks.com


# Xanax. 3/1/2007 2:15 PM Xanax.

Xanax.


# Xenical. 3/11/2007 10:54 AM Xenical.

Xenical.


# Lipitor. 3/16/2007 2:22 AM Lipitor.

Lipitor.


# Diazepam. 3/17/2007 11:27 AM Diazepam.

Diazepam.


# Ambien. 3/19/2007 7:41 PM Ambien.

Ambien.

Comments have been closed on this topic.