using System.Threading.Tasks;
using System.Linq.Expressions;
using System.Collections.Generic;
public static void Main()
public class HandlerAttribute : Attribute { }
public class SomeType1 { }
public class SomeType2 { }
protected void Register<T>(Action<T> action)
Console.WriteLine($"Registering {action}");
public sealed class MyClass : Base
Ext.RegisterHandlers(this);
private void Handle(SomeType1 t) => Console.WriteLine("Invoking Handle(SomeType1)");
private void Handle(SomeType2 t) => Console.WriteLine("Invoking Handle(SomeType2)");
private static class Cache<T> where T : Base
public static readonly Action<T> Instance = CreateInstance();
private static Action<T> CreateInstance()
var methods = typeof(T).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.GetCustomAttribute<HandlerAttribute>() != null);
var registerMethod = typeof(Base).GetMethod("Register", BindingFlags.NonPublic | BindingFlags.Instance);
var instanceParameter = Expression.Parameter(typeof(T));
var blockItems = new List<Expression>();
foreach (var method in methods)
if (method.IsGenericMethod || method.GetParameters().Length != 1 || method.ReturnType != typeof(void))
throw new Exception($"Invalid method signature for method {method}");
var parameterType = method.GetParameters()[0].ParameterType;
var typedRegisterMethod = registerMethod.MakeGenericMethod(parameterType);
var delegateType = typeof(Action<>).MakeGenericType(parameterType);
var delegateParameter = Expression.Parameter(parameterType);
var delegateConstruction = Expression.Lambda(delegateType, Expression.Call(instanceParameter, method, delegateParameter), delegateParameter);
var methodCall = Expression.Call(instanceParameter, typedRegisterMethod, new[] { delegateConstruction });
blockItems.Add(methodCall);
var compiled = Expression.Lambda<Action<T>>(Expression.Block(blockItems), instanceParameter).Compile();
public static void RegisterHandlers<T>(T t) where T : Base