using System.Collections.Concurrent;
using System.Linq.Expressions;
public interface ITechnicianApi101
public interface ITechnicianApi103 : ITechnicianApi101
string Method3(int input1, string input2);
public class TechnicianManager : ITechnicianApi101, ITechnicianApi103
private static readonly ConcurrentDictionary<string, object> _methodCallersByApiType =
new ConcurrentDictionary<string, object>();
public TechnicianManager WithVersion(string version)
_version = typeof(ITechnicianApi101);
_version = typeof(ITechnicianApi103);
throw new NotSupportedException("Nope");
var caller = (Expression<Func<TechnicianManager, object>>)BuildMethodCaller("Method1");
return (string)caller.Compile().Invoke(this);
var caller = (Expression<Func<TechnicianManager, object>>)BuildMethodCaller("Method2");
return (string)caller.Compile().Invoke(this);
public string Method3(int input1, string input2)
var caller = (Expression<Func<TechnicianManager, int, string, object>>)BuildMethodCaller("Method3");
return (string)caller.Compile().Invoke(this, input1, input2);
string ITechnicianApi101.Method1()
string ITechnicianApi101.Method2()
string ITechnicianApi103.Method2()
string ITechnicianApi103.Method3(int input1, string input2)
private object BuildMethodCaller(string methodName)
return _methodCallersByApiType.GetOrAdd(_version.Name + "." + methodName, key =>
var managerParameter = Expression.Parameter(typeof(TechnicianManager), "manager");
var apiTypedManager = Expression.Convert(managerParameter, _version);
var method = GetMethod(apiTypedManager.Type, methodName);
var parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToList();
var parameters = parameterTypes.Select((t, i) => Expression.Parameter(t, "param" + i)).ToList();
var methodCall = Expression.Call(apiTypedManager, method, parameters);
parameterTypes.Insert(0, managerParameter.Type);
parameterTypes.Add(typeof(object));
parameters.Insert(0, managerParameter);
var methodCallLambda = Expression.Lambda(
Expression.GetFuncType(parameterTypes.ToArray()),
private static MethodInfo GetMethod(Type apiVersion, string name)
return apiVersion.GetMethod(name) ?? GetMethod(apiVersion.GetInterfaces().First(), name);
public static void Main()
TechnicianManager manager101 = new TechnicianManager().WithVersion("101");
Console.WriteLine("101.Method1: " + manager101.Method1());
Console.WriteLine("101.Method2: " + manager101.Method2());
TechnicianManager manager103 = new TechnicianManager().WithVersion("103");
Console.WriteLine("103.Method1: " + manager103.Method1());
Console.WriteLine("103.Method2: " + manager103.Method2());
Console.WriteLine("103.Method3: " + manager103.Method3(123, "test"));