Optimize C# Reflection Up to 10 Times by Using Delegates

Optimize C# Reflection Up to 10 Times by Using Delegates

Developers love reflection because it can save them numerous hours of boilerplate code. But developers also know reflection is slow and it should be used with caution. However, frameworks like ASP.NET Core constantly use the technique to provide their features. So how do they do it?

My name is Ivaylo Kenov, and I am a guest writer at Automate the Planet. I am currently working as a CTO in a large software development team, and I am a huge fan of high-quality software development and clean code. For this reason, I created My Tested ASP.NET – a collection of open-source fluent testing libraries for ASP.NET. In my project, I also use reflection, but I try to optimize it to be as fast as possible. 

In this blog post, I would like to share with you one of the cool techniques I learned from the ASP.NET Core code while developing my tools. I optimized my test frameworks more than five times by simply using delegates for my reflection code. I also explain the concepts from this article in this video, so if you prefer to watch me code it live, instead of reading a text, make sure you check it out.

Reflection Definition

Reflection is when managed code can read its own metadata to find assemblies. Essentially, it allows code to inspect other code within the same system. With reflection in C#, you can dynamically create an instance of a type and bind that type to an existing object. Moreover, you can get the type from an existing object and access its properties. When you use attributes in your code, reflection gives you access as it provides objects of Type that describe modules, assemblies, and types.

You need to use Reflection when you want to inspect the contents of an assembly. For example, you can get all members of the object by typing “.” before an object when viewing your Visual Studio editor IntelliSense.

The Plain Old Reflection Code

Let’s first see one of the slowest reflection features – invoking a method. Notably, we are going to invoke a property getter from a class.

public class HomeController
{
    public IDictionary<string, object> Data { get; set; }
}

Imagine a situation in which you will need to get the Data dictionary values numerous times via reflection. For example, in the context of a web server processing thousands of requests per minute. This is the straightforward reflection way to retrieve this value.

var homeController = new HomeController();
var homeControllerType = homeController.GetType();
var property = homeControllerType.GetProperty(nameof(HomeController.Data));
var getMethod = property.GetMethod;
var dict = (IDictionary<string, object>)getMethod.Invoke(homeController, Array.Empty<object>());

Now Let’s Optimize it

The idea behind the optimization of the code above is to create a delegate, which will invoke the getter for us. A delegate is way faster than the reflection Invoke method. 

var getMethod = property.GetMethod;
var declaringClass = property.DeclaringType;
var typeOfResult = typeof(IDictionary<string, object>);
// Func<ControllerType, TResult>
var getMethodDelegateType = typeof(Func<,>).MakeGenericType(declaringClass, typeOfResult);
// c => c.Data
var getMethodDelegate = getMethod.CreateDelegate(getMethodDelegateType);
var finalDelegate = (Func < HomeController, IDictionary<string, object>)getMethodDelegate;
var dict = finalDelegate(homeController);

Unfortunately, creating the delegate is a slow operation. We need to cache it and reuse it if we want to be super-fast. Additionally, if we are going to use this optimization technique with different classes, we need to make it more abstract. The following PropertyHelper class is a good enough solution.

public class PropertyHelper
{
    private static readonly ConcurrentDictionary<Type, PropertyHelper[]> Cache
    = new ConcurrentDictionary<Type, PropertyHelper[]>();
    private static readonly MethodInfo CallInnerDelegateMethod =
    typeof(PropertyHelper).GetMethod(nameof(CallInnerDelegate), BindingFlags.NonPublic | BindingFlags.Static);
    public string Name { get; set; }
    public Func<object, object> Getter { get; set; }
    public static PropertyHelper[] GetProperties(Type type)
    => Cache
    .GetOrAdd(type, _ => type
    .GetProperties()
    .Select(property =>
    {
        var getMethod = property.GetMethod;
        var declaringClass = property.DeclaringType;
        var typeOfResult = property.PropertyType;
        // Func<Type, TResult>
        var getMethodDelegateType = typeof(Func<,>).MakeGenericType(declaringClass, typeOfResult);
        // c => c.Data
        var getMethodDelegate = getMethod.CreateDelegate(getMethodDelegateType);
        // CallInnerDelegate<Type, TResult>
        var callInnerGenericMethodWithTypes = CallInnerDelegateMethod
    .MakeGenericMethod(declaringClass, typeOfResult);
        // Func<object, object>
        var result = (Func<object, object>)callInnerGenericMethodWithTypes.Invoke(null, new[] { getMethodDelegate });
        return new PropertyHelper
        {
            Name = property.Name,
            Getter = result
        };
    })
    .ToArray());
    // Called via reflection.
    private static Func<object, object> CallInnerDelegate<TClass, TResult>(
    Func<TClass, TResult> deleg)
    => instance => deleg((TClass)instance);
}

The above class will create for us ready to be used delegates and will cache them for each type we provide. Notice how we need to wrap our strongly-typed delegate in a more abstract one to make our cache work.

var controller = new HomeController();
var properties = PropertyHelper.GetProperties(typeof(HomeController));
foreach (var property in properties)
{
    Console.WriteLine(property.Name);
    Console.WriteLine(property.Getter(controller));
}

Conclusion

By creating and reusing delegates, you can optimize your reflection code multiple times. Some of my measurements showed more than 1000% performance improvement, and that’s a huge one. Try it the next time you are facing reflection, and you will be amazed by the results!

If you liked this article and want to learn more advanced C# techniques, make sure you check out My Tested ASP.NET’s YouTube channel. Happy hacking!

Related Articles

Development

Which Works Faster- Null Coalescing Operator or GetValueOrDefault or Conditional Operator

After my article Top 15 Underutilized Features of .NET provoked an interesting discussion. I was curious to learn which method is faster- ?? (null coalescing op

Which Works Faster- Null Coalescing Operator or GetValueOrDefault or Conditional Operator

Development

Get Property Names Using Lambda Expressions in C#

In this article, I am going to present to you how to get property and method names from lambda expressions. You can pass properties and methods as methods' para

Get Property Names Using Lambda Expressions in C#

Development

Neat Tricks for Effortlessly Formatting Currency in C#

In the article, I am going to present to you a few ways how to create a handy class that effortlessly formatting currency in C#. Initially, I came up with the i

Neat Tricks for Effortlessly Formatting Currency in C#

Development

Types Of Code Coverage- Examples In C#

Code coverage analysis is used to measure the quality of software testing, usually using dynamic execution flow analysis. There are many different types of code

Types Of Code Coverage- Examples In C#

Development, Resources

Most Complete MSTest Unit Testing Framework Cheat Sheet

An essential part of every UI test framework is the usage of a unit testing framework. One of the most popular ones in the .NET world is MSTest. However, you ca

Most Complete MSTest Unit Testing Framework Cheat Sheet

Development

Embracing Non-Nullable Reference Types in C# 8

Microsoft launched its latest version of the C# language – C# 8.0 – in September 2019. It came with many new features or improvements like readonly struct membe

Embracing Non-Nullable Reference Types in C# 8
Anton Angelov

About the author

Anton Angelov is Managing Director, Co-Founder, and Chief Test Automation Architect at Automate The Planet — a boutique consulting firm specializing in AI-augmented test automation strategy, implementation, and enablement. He is the creator of BELLATRIX, a cross-platform framework for web, mobile, desktop, and API testing, and the author of 8 bestselling books on test automation. A speaker at 60+ international conferences and researcher in AI-driven testing and LLM-based automation, he has been recognized as QA of the Decade and Webit Changemaker 2025.