Fast Reflection Library

This is a project I've created in CodePlex under Microsoft Public License (Ms-PL). You can find it here.

Simple Usage

Reflection is one of the most important features of .NET platform. The way of accessing/assigning a property or invoking a method dynamically is widely used by numerous projects. As we all know, invoke-by-reflection is much less efficient than direct access. FastReflectionLib provide the same as part of the refection features like executing method dynamically but give simple and faster implementations. It can be use as the foundation of reflection-based scenarios such as ORM framework.

Please look at the code snippets below:

using System;
using System.Reflection;
using FastReflectionLib;

namespace SimpleConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            PropertyInfo propertyInfo = typeof(string).GetProperty("Length");
            MethodInfo methodInfo = typeof(string).GetMethod("Contains");

            string s = "Hello World!";

            // get value by normal reflection
            int length1 = (int)propertyInfo.GetValue(s, null);
            // get value by the extension method from FastReflectionLib,
            // which is much faster
            int length2 = (int)propertyInfo.FastGetValue(s);

            // invoke by normal reflection
            bool result1 = (bool)methodInfo.Invoke(s, new object[] { "Hello" });
            // invoke by the extension method from FastReflectionLib,
            // which is much faster
            bool result2 = (bool)methodInfo.FastInvoke(s, new object[] { "Hello" });
        }
    }
}

When we get the MethodInfo object, we can call the Invoke method to execute the method. FastReflectionLib contains several extension methods on these types (such as MethodInfo), so we can just put "Fast" before the method to replace the build-in ones. Comparing with the build-in functions, these new implementations get big performance improvements.

Use the worker object directly

For example, the extension method "FastInvoke" defined for MethodInfo type gets the corresponding worker object by the MethodInfo instance as the key from cache, and execute the Invoke method in the worker object. Apparently keeping the worker object and use it directly after getting it would give us better perfermance than retrieving it from cache again and again.

Here's the list of worker types for each XxxInfo type:

  • PropertyInfo: IPropertyAccessor
  • MethodInfo: IMethodInvoker
  • ConstructorInfo: IConstructorInvoker
  • FieldInfo: IFieldAccessor

We can get an IMethodInvoker object from FastReflectionCaches.MethodInvokerCache by a MethodInfo object:

static void Execute(MethodInfo methodInfo, object instance, int times)
{ 
    IMethodInvoker invoker = FastReflectionCaches.MethodInvokerCache.Get(methodInfo);
    object[] parameters = new object[0];
    for (int i = 0; i < times; i++)
    {
        invoker.Invoke(instance, parameters);
    }
}

Default implementations of worker types and extensions

FastReflectionLib has already provide default implementations for each worker interfaces. These implementations generate lambda expressions and compile them (by call Compile method) to delegate objects with the same signatures as the corresponding reflection methods. The implementation is simle, general and safe than directly Emit and the performance is good enough for most scenarios (please refer to the Benchmarks section).

But it's not the most efficient way to do so. The best optimized way by Emit could get better performance than directly access the members in code (Dynamic Reflection Library is one of these implementations). You can build your one worker interface implementation and factory to replace the build-in ones if necessary:

public class BetterPropertyAccessor : IPropertyAccessor
{
    public BetterPropertyAccessor(PropertyInfo propertyInfo) { ... }

    ...
}

public class BetterPropertyAccessorFactory :
    IFastReflectionFactory<PropertyInfo, IPropertyAccessor>
{
    public IPropertyAccessor Create(PropertyInfo key)
    {
        return new BetterPropertyAccessor(key);
    }
}

class Program
{
    static void Main(string[] args)
    {
        FastReflectionFactories.PropertyAccessorFactory =
            new BetterPropertyAccessorFactory();

        ...
    }
}

Default implementaions of caches and extensions

FastReflectionLib uses the caches based on System.Collections.Generic.Dictionary<TKey, TValue>. When we invoke a FastXxx extension method, the library would retrieve the worker object from the cache container and execute it. If the worker object cannot be found in the cache, a new one would be created by the corresponding factory. In the Benchmarks section we can found that considerable time is "wasted" in searching the cache.

If you have better cache implementation you can replace the build-in one as following:

public class BetterMethodInvokerCache :
    IFastReflectionCache<MethodInfo, IMethodInvoker>
{
    public IMethodInvoker Get(MethodInfo key) { ... }
}

class Program
{
    static void Main(string[] args)
    {
        FastReflectionCaches.MethodInvokerCache = 
            new BetterMethodInvokerCache();

        ...
    }
}

Cache the worker objects by your own

FastReflectionLib uses the reflection objects (such as PropertyInfo) as key to cache the worker object (such as PropertyAccessor). But in some scenarios, we can cache the worker objects by your own. Here's an sample web site "CustomCache" release with the source of FastReflectionLib, which contains a series of overloaded FastEval extension methods that can be used as an efficient replacement for the build-in Eval method in some cases. The sample:

  • uses both the Type of class and the property name as a "joint key" to cache the PropertyAccessor objects.
  • uses PropertyAccessor to get the value of property defined in an "anonymous object".
  • caches in the scope of "page" instead of the whole AppDomain. 

Benchmarks

A benchmark project is provided with the source of FastReflectionLib. Here's part of the results (the benchmarks were run on my laptop with release build):

Execute the following method 1,000,000 times:

public class Test
{
    public void MethodWithArgs(int a1, string a2) { }
}
The way to execute Time elapsed (in second)
Directly invoke 0.0071397
Build-in reflection 1.4936181
MethodInvoker.Invoke 0.0468326
FastInvoke 0.1373712

2 Comments

Comments have been disabled for this content.