When you work with dynamic types or runtime code generation, reflection quickly becomes both powerful and confusing. I faced a real puzzle recently β how to invoke a generic method that itself returns a generic Action delegate, and then call that delegate at runtime.
βοΈ The Setup
Suppose you have a helper like this:
public static class FastInvoke
{
public static Action<T, object> BuildUntypedSetter<T>(MemberInfo member)
{
var instance = Expression.Parameter(typeof(T), "obj");
var value = Expression.Parameter(typeof(object), "val");
var access = Expression.MakeMemberAccess(instance, member);
var convert = Expression.Convert(value, ((PropertyInfo)member).PropertyType);
var body = Expression.Assign(access, convert);
return Expression.Lambda<Action<T, object>>(body, instance, value).Compile();
}
}
This dynamically builds a setter for any property on any type β a neat reflection shortcut that compiles to IL at runtime.
π§© The Problem
Now imagine weβre building a dynamic type with TypeBuilder, and we want to call the setter without knowing the type at compile time:
var type = MyTypeBuilder.CompileResultType(dtTempAttendance, true);
var obj = Activator.CreateInstance(type);
var prop = type.GetProperty("EmployeeName");
We find the method via reflection:
var method = typeof(FastInvoke)
.GetMethod("BuildUntypedSetter")
.MakeGenericMethod(type);
var setter = method.Invoke(null, new[] { prop }) as Action<object, object>;
But β surprise!setter is always null.
Why? Because Action<SomeType, object> β Action<object, object>.
Generic delegate types are not automatically compatible.
π The Discovery
The fix wasnβt to force-cast (which fails), but to treat the result as a Delegate and invoke it dynamically.
var del = method.Invoke(null, new[] { prop }) as Delegate;
del.DynamicInvoke(obj, "Test Value");
That worked β though it sacrifices some of the βfastβ in FastInvoke.
If performance matters, the proper way is to generate a uniform wrapper (e.g., a cached dictionary of compiled lambdas per type) instead of generic invocation per call.
π§° Practical Takeaway
- Generic Actions are not covariant β
Action<T, object>andAction<object, object>are distinct. - If you only know the type at runtime, use
DelegateorDynamicInvoke(). - To retain performance, build a delegate cache keyed by type once, not per call.
π‘ Bonus Tip: When Reflection Meets JSON
In my actual scenario, I sidestepped the reflection headache entirely by serializing the data into JSON and parsing it client-side. Sometimes, the practical solution is simpler than the theoretical one!
Author: Algolassi
Posted on: October 9, 2025
Category: C#, Reflection, Dynamic Programming
Tags: c#, reflection, generics, delegates
Ask AlgoLassi and get an answer plus the tutorials worth studying next.
π¬ Comments
Sign in with Google to publish immediately, or comment anonymously and wait for approval.
Comments will appear here when available.