Using Generated Methods Instead of Reflection

Introduction

It is a common thing to say that reflection is slow.You will find tons of posts saying this, and I generally tend to agree to them, although in most cases we generally don’t need to care that much – in fact, so many libraries and frameworks that we rely on daily depend on it!

When we talk about reflection, what is it that we talk about, normally? I’d say:

  • Getting an instance’s type, or base type, or implemented interfaces
  • Getting a list of properties, or fields
  • Setting values for properties, or fields
  • Finding methods
  • Invoking methods

Some things we can do to speed up the process include caching the PropertyInfo and FieldInfo objects, so that there is no need to retrieve them again and again from the declaring Type, but when it comes to getting and setting actual values, no caching can help here.

One thing that we can do, though, is generate code for actually accessing the properties/fields for us, in a strongly typed manner, without using reflection. This incurs in a startup cost, because of the overhead associated with the code generation, but after that it becomes much faster than the reflection-based code. Without more ado, let’s jump to the code.

First, some base class with just a single method for representing a simple setter operation:

public abstract class Setter {    

public abstract void Set(object obj, string propertyName, object value);

}

Simple, right? We take a target object (obj), a property (propertyName) and some value, and we’re good.

Reflection

The simplest, reflection-based implementation could look like this (ReflectionSetter):

public sealed class ReflectionSetter : Setter

{

     public override void Set(object obj, string propertyName, object value)

     {

         ArgumentNullException.ThrowIfNull(obj);

         ArgumentNullException.ThrowIfNull(propertyName);

         var property = obj.GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty);

         if (property != null)

         {

             property.SetValue(obj, value, null);

         }

         else

         {

             throw new InvalidOperationException(“Property not found.”);

         }

     } }

As you can see, nothing special here, the code does what you might expect: only public, instance properties with both a setter and a getter are looked for, and if not found, an exception is thrown.

Reflection With Caching

Another, slightly more elaborared version, which caches the PropertyInfo objects, but which requires that all types be initialized prior to being used (CachedReflectionSetter):

public sealed class CachedReflectionSetter : Setter

{

     private readonly Dictionary<Type, Dictionary<string, PropertyInfo>> _properties = new Dictionary<Type, Dictionary<string, PropertyInfo>>();

     public void Initialize(Type type)

     {

         ArgumentNullException.ThrowIfNull(type);

         this._properties[type] = new Dictionary<string, PropertyInfo>();

         foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty))

         {

             this._properties[type][prop.Name] = prop;

         }

     }

     public override void Set(object obj, string propertyName, object value)

     {

         ArgumentNullException.ThrowIfNull(obj);

         ArgumentNullException.ThrowIfNull(propertyName);

         var property = this.GetPropertyFor(obj.GetType(), propertyName);

         if (property != null)

         {

             property.SetValue(obj, value, null);

         }

         else

         {

             throw new InvalidOperationException(“Property not found.”);

         }

     }

     private PropertyInfo? GetPropertyFor(Type type, string propertyName)

     {

         if (this._properties.TryGetValue(type, out var properties))

         {

             if (properties.TryGetValue(propertyName, out var prop))

             {

                 return prop;

             }

         }

         return null;

     } }

The only difference between the two is that this one does not retrieve the PropertyInfo objects on the fly from the passed instance, instead it does so from a dictionary. But one must not forget to call its Initialize method with all the Types that we want to use it with, otherwise, it will not know its properties.

Compiled Lambda

Now things get a little more complext, as we want to get away from reflection – at leat at the runtime level, when we are setting the value for the property, which is something that can potentially happen a lot of times during the lifefime of our application. So, enter the third iteration (CompiledSetter):

public sealed class CompiledSetter : Setter {

     private readonly Dictionary<Type, Dictionary<string, Delegate>> _properties = new Dictionary<Type, Dictionary<string, Delegate>>();

     public void Initialize(Type type)

     {

         ArgumentNullException.ThrowIfNull(type);

         this._properties[type] = new Dictionary<string, Delegate>();

         foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty))

         {

            this.GenerateSetterFor(type, prop);

         }

     }

     public override void Set(object obj, string propertyName, object value)

     {

         ArgumentNullException.ThrowIfNull(obj);

         ArgumentNullException.ThrowIfNull(propertyName);

         var action = this.GetActionFor(obj.GetType(), propertyName);

         if (action is Action<object, object> act)

         {

                act(obj, value);

         }

         else

         {

             throw new InvalidOperationException(“Property not found.”);

         }

     }

     private void GenerateSetterFor(Type type, PropertyInfo property)

     {

         var propertyName = property.Name;

         var propertyType = property.PropertyType;

         var parmExpression = Expression.Parameter(typeof(object), “it”);

         var castExpression = Expression.Convert(parmExpression, type);

         var propertyExpression = Expression.Property(castExpression, propertyName);

         var valueExpression = Expression.Parameter(typeof(object), propertyName);

         var operationExpression = Expression.Assign(propertyExpression, Expression.Convert(valueExpression, propertyType));

         var lambdaExpression = Expression.Lambda(typeof(Action<,>).MakeGenericType(typeof(object), typeof(object)), operationExpression, parmExpression, valueExpression);

         var action = lambdaExpression.Compile();

         this._properties[type][propertyName] = action;

     }

     private Delegate? GetActionFor(Type type, string propertyName)

     {

         if (this._properties.TryGetValue(type, out var properties))

         {

             if (properties.TryGetValue(propertyName, out var action))

             {

                 return action;

             }

         }

         return null;

     } }

The approach here is to generate an expression for a lambda that accesses the property that we want to access and then compile it. The problem is that it would normally produce something like this:

Action<MyEntity> (it) => it.MyProperty = MyValue;

Which is not what we want, essentially, because with this we cannot call it dynamically – we do not know, at runtime, the MyEntity type or the type of MyProperty! What we want, and always have, is object, so, instead, we want to go with something along these lines:

Action<object, object> (it, prop) => ((MyEntity) it).MyProperty = (MyPropertyType)prop;

This way, we can always call the generated lambda with the target object and we

And it works like a charm! Winking smile

Performance Tests

I did a few tests with BenchmarkDotNet:

public class Entity

{

public int Id { get; set; }

public string Name { get; set; }

}

public class Test {     static CompiledSetter compiledSetter = new CompiledSetter();     static ReflectionSetter reflectionSetter = new ReflectionSetter();     static CachedReflectionSetter cachedReflectionSetter = new CachedReflectionSetter();     static Entity[]? entities = null;     [GlobalSetup]     public static void Setup()     {         compiledSetter.Initialize(typeof(Entity));         cachedReflectionSetter.Initialize(typeof(Entity));         entities = Enumerable.Range(0, 200).Select(x => new Entity()).ToArray();     }     private static void Common(Setter setter)     {         for (var i = 0; i < entities?.Length; i++)         {             setter.Set(entities[i], nameof(Entity.Id), i);             setter.Set(entities[i], nameof(Entity.Name), i.ToString());         }     }     [Benchmark]     public static void TestCompiled() => Common(compiledSetter);     [Benchmark]     public static void TestReflection() => Common(reflectionSetter);     [Benchmark]     public static void TestCachedReflection() => Common(cachedReflectionSetter); }

Essentially, the tests generated a few (200) entities with two properties, the setters that required initialization were initialized, and then the properties were set in a loop.

The results I got were:

results

As you can see, the compiled version (CompiledSetter) outperformed the other two by a great margin. There’s also a small benefit in caching the PropertyInfo, as CachedReflectionSetter version arrived second on the results.

Conclusion

It is clear that reflection is indeed slow and a few things can definitely be done about it. I showed here setting property values, but the same logic can be done for getting them, and I’m happy to provide the code to anyone interested. If you wish to discuss any aspects of this, or have any questions, just give me a shout. As always, hope you find this useful!

Published by

Ricardo Peres

Tech Lead at RedLight Software.

Leave a Reply

Your email address will not be published. Required fields are marked *