Want to show your appreciation? Please to my charity.

Friday, May 08, 2009

Strong Typed, High Performance Reflection with C# Delegate

Update: Open source project SharpCut delivers a less than 50K library which does what described here in one line plus much more. Check it out.

Content

  1. Inspiration: C#.Net Calling Grandparent's Virtual Method (base.base in C#)
  2. Prototype: Strong Typed, High Performance Reflection with C# Delegate (Part I) <= you are here
  3. Performance: Strong Typed, High Performance Reflection with C# Delegate (Part II)
  4. Library Usage: Strong Typed, High Performance Reflection with C# Delegate (Part III) 

The process of finding a solution for base.base.VirtualMethod() in C# inspired me to create utility/extension method for reflection. The goal was to use delegate instead of MethodInfo.Invoke or DynamicMethod.Invoke. Using the Invoke method requires boxing all value type to object, create an object array, unboxing before call the actual method, boxing/unboxing the return value if necessary and cast the return value to the excepted data type. Very involved indeed.

If the MethodInfo or DynamicMethod is reused to make the call, using Invoke method is costly and error prone. Can we create a Delegate object out of them so that it can be strong typed, and hopefully more efficient.

DynamicMethod class has overloaded methods named CreateDelegate. And Delegate class too has CreateDelegate methods that takes MethodInfo, so .Net framework does have the weapon we need.

Let's go step by step.

Utility method to create non-virtual invoke DynamicMethod from MethodInfo

Fist, extract the part that create the DynamicMethod in my last post and enhance it to work with any given MethodInfo object.

        public static DynamicMethod CreateNonVirtualDynamicMethod(this MethodInfo method)
        {
            int offset = (method.IsStatic ? 0 : 1);
            var parameters = method.GetParameters();
            int size = parameters.Length + offset;
            Type[] types = new Type[size];
            if (offset > 0) types[0] = method.DeclaringType;
            for (int i = offset; i < size; i++)
            {
                types[i] = parameters[i - offset].ParameterType;
            }

            DynamicMethod dynamicMethod = new DynamicMethod(
                "NonVirtualInvoker_" + method.Name, method.ReturnType, types, method.DeclaringType);
            ILGenerator il = dynamicMethod.GetILGenerator();
            for (int i = 0; i < types.Length; i++) il.Emit(OpCodes.Ldarg, i);
            il.EmitCall(OpCodes.Call, method, null);
            il.Emit(OpCodes.Ret);
            return dynamicMethod;
        }

With this tool, we can slim down our implementation of base.base in class C quite a bit.


    class C : B
    {
        private static readonly DynamicMethod baseBaseFoo;
        static C()
        {
            MethodInfo fooA = typeof(A).GetMethod("foo", BindingFlags.Public | BindingFlags.Instance);
            baseBaseFoo = fooA.CreateNonVirtualDynamicMethod();
        }
        public override string foo() { return (string)baseBaseFoo.Invoke(null, new object[] { this }); }
    }

Create Delegate from DynamicMethod

As we said in the beginning that the DynamicMethod.Invoke is verbose and and inefficient. The solution is to create a Delegate out of DynamicMethod and use the Delegate.  We can do it right inside the class C, and you can see that the call to the baseBaseFoo now is short, clean and strong typed. We'll discuss the performance benefit in next post.


    class C : B
    {
        private static readonly Func<A, string> baseBaseFoo;
        static C()
        {
            MethodInfo fooA = typeof(A).GetMethod("foo", BindingFlags.Public | BindingFlags.Instance);
            baseBaseFoo = 
                (Func<A, string>)fooA.CreateNonVirtualDynamicMethod()
                .CreateDelegate(typeof(Func<A, string>));
        }
        public override string foo() { return baseBaseFoo(this); }
    }

This is great with one downside is that we had to cast here and there when we create the Delegate. Can we extract this logic into a generic method so that in class C I can simply do this?

    baseBaseFoo = GetNonVirtualInvoker<Func<A, string>>(fooA);

My first attempt was not successful. See the code below


        public static TDelegate GetNonVirtualInvoker<TDelegate>(this MethodInfo method)
            where TDelegate : Delegate
        {
            var dynamicMethod = CreateNonVirtualDynamicMethod(method);
            return (TDelegate)dynamicMethod.CreateDelegate(typeof(TDelegate));
        }

It would be most ideal if this works so that the generic method only takes Delegate as type parameter. But the compiler give me a red line under the constrain type Delegate and complained that "Constraint cannot be special class 'System.Delegate'". Why Microsoft? Come and vote for the change here!

My second attempt is to use cast. But the compiler was still unhappy with error message: "Cannot cast expression of type 'System.Delegate' to 'TDelegate'".

        public static TDelegate GetNonVirtualInvoker<TDelegate>(this MethodInfo method)
        {
            var dynamicMethod = CreateNonVirtualDynamicMethod(method);
            return (TDelegate)dynamicMethod.CreateDelegate(typeof(TDelegate));
        }

This is very annoying. Is something wrong with the framework/language design? The workaround turn out to be very simply. Cast to object then back to TDelegate. (Update 5/15: actually there is a workaround to avoid double cast)

Finally the code below works:
        public static TDelegate GetNonVirtualInvoker<TDelegate>(this MethodInfo method)
        {
            var dynamicMethod = CreateNonVirtualDynamicMethod(method);
            return (TDelegate)(object)dynamicMethod.CreateDelegate(typeof(TDelegate));
        }

Thus the class C can be further simplified to:

    class C : B
    {
        private static readonly Func<A, string> baseBaseFoo;
        static C()
        {
            MethodInfo fooA = typeof(A).GetMethod("foo", BindingFlags.Public | BindingFlags.Instance);
            baseBaseFoo = fooA.GetNonVirtualInvoker<Func<A, string>>();
        }
        public override string foo() { return baseBaseFoo(this); }
    }

One stop shop extension method returns Delegate from type and method name

Now look at the goal set in the last post and repeated below. The class C must be further cut down to achieve the goal.

    class C : B
    {
        private static readonly Func<A, string> baseBaseFoo = 
            typeof(A).GetNonVirtualInvoker<Func<A, string>>("foo");
        public override string foo() { return baseBaseFoo(this); }
    }

Indeed, getting the MethodInfo object from a given type is never a one liner, it becomes verbose when method is overloaded thus parameters types matching is necessary. Things are getting more interesting now. Delegate has the precise information about the signature of method. Our utility method can be further enhanced to find the method from a given type with just a method name, because the parameter information can be found in the Delegate type.

        public static TDelegate GetNonVirtualMethod<TDelegate>(this Type type, string name)
        {
            Type delegateType = typeof(TDelegate);
            if (!typeof(MulticastDelegate).IsAssignableFrom(delegateType))
            {
                throw new InvalidOperationException(
                    "Expecting type parameter to be a Delegate type, but got " +
                    delegateType.FullName);
            }
            var invoke = delegateType.GetMethod("Invoke");
            ParameterInfo[] parameters = invoke.GetParameters();
            int size = parameters.Length - 1;
            Type[] types = new Type[size];
            for (int i = 0; i < size; i++)
            {
                types[i] = parameters[i + 1].ParameterType;
            }
            var method = type.GetMethod(name, 
                BindingFlags.Public | BindingFlags.NonPublic | 
                BindingFlags.Instance | BindingFlags.InvokeMethod, 
                null, types, null);
            if (method == null) return default(TDelegate);
            var dynamicMethod = CreateNonVirtualDynamicMethod(method);
            return (TDelegate)(object)dynamicMethod.CreateDelegate(delegateType);
        }

This extension method let you created a Delegate that can make non-virtual invocation to the named method of given type. The method parameters matches the signature of the Delegate. For example, instance method ClassA.Method1(string, int) matches Delegate(ClassA, string, int). The extension method started with making sure the type parameter is indeed a Delegate type, then retrieve the parameter types from the Delegate's Invoke method, then lookup the method in the given type, create dynamic method and finally create the Delegate.

Continue...

The complete code used in this blog can be found here. The code is the result of inception and prototype of the Common.Reflection. In next few posts, we'll implement formal extension methods with enhanced features and compare the performance between direct method call, reflection invoke and Delegate call.

11 comments:

Mithun Mithun said...



Given so much information in it. its very useful .perfect explanation about Dot net framework.Thanks for your valuable information. dot net training in velachery | dot net training in chennai

Pavithra M said...

It is really a great work and the way in which u r sharing the knowledge is excellent.
Thanks for helping me to understand basic concepts. As a beginner in Dot Net programming your post help me a lot.Thanks for your informative article. Dot Net Training in chennai | Dot Net Training in velachery

Kingsly David said...

Great post! Thanks for sharing with us.

Angularjs Training in Chennai | Web Designing Training in Chennai

sai said...

We are a group of volunteers and starting a new initiative in a community. Your blog provided us valuable information to work on.You have done a marvellous job!
Click here:
angularjs training in bangalore
Click here:
angularjs training in chennai

john jersy said...

Really great post, I simply unearthed your site and needed to say that I have truly appreciated perusing your blog entries.
Click here:
Microsoft azure training in velarchery
Click here:
Microsoft azure training in sollinganallur

ummayashri said...

This looks absolutely perfect. All these tiny details are made with lot of background knowledge. I like it a lot. 

Blueprism training in Chennai

Blueprism training in Bangalore

Blueprism training in Pune

Blueprism online training

Blueprism training in tambaram

johnsy sai said...

The knowledge of technology you have been sharing thorough this post is very much helpful to develop new idea. here by i also want to share this.
Devops training in sholinganallur

amala jst said...

A very nice guide. I will definitely follow these tips. Thank you for sharing such detailed article. I am learning a lot from you.

rpa training in electronic-city | rpa training in btm | rpa training in marathahalli | rpa training in pune

nivatha said...

Really great post, I simply unearthed your site and needed to say that I have truly appreciated perusing your blog entries. I want to say thanks for great sharing.
Data Science training in rajaji nagar | Data Science with Python training in chenni
Data Science training in electronic city | Data Science training in USA
Data science training in pune | Data science training in kalyan nagar

simbu said...

I really appreciate this post. I’ve been looking all over for this! Thank goodness I found it on Bing. You’ve made my day! Thx again!
java training in annanagar | java training in chennai

java training in chennai | java training in electronic city

thulasi ragini said...


Whoa! I’m enjoying the template/theme of this website. It’s simple, yet effective. A lot of times it’s very hard to get that “perfect balance” between superb usability and visual appeal. I must say you’ve done a very good job with this.

Selenium Interview Questions and Answers

Best Selenium Training in Chennai | Selenium Training Institute in Chennai | Besant Technologies

Selenium Training in Bangalore | Best Selenium Training in Bangalore

Free Selenium Tutorial |Selenium Webdriver Tutorial |For Beginners

Post a Comment