Skip to content

Instantly share code, notes, and snippets.

@Tamschi
Created October 18, 2013 08:33
Show Gist options
  • Save Tamschi/7038408 to your computer and use it in GitHub Desktop.
Save Tamschi/7038408 to your computer and use it in GitHub Desktop.
Automatic and manual dynamic dispatch. The automatic version doesn't work with invisible types.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace DynamicDispatch
{
class Base { }
class Generic<T> : Base { }
#if !ALL_VISIBLE
static class Hiding
{
private class Invisible { }
public static Base GetInvisible()
{ return new Generic<Invisible>(); }
}
#endif
class Program
{
public static void Main(string[] args)
{ new Program().Run(args); }
void Run(string[] args)
{
var list = new List<Base>()
{
new Generic<int>(),
new Generic<bool>(),
new Generic<string>(),
new Generic<Base>(),
new Generic<Generic<int>>(),
new Generic<object>(),
#if !ALL_VISIBLE
Hiding.GetInvisible(),
Hiding.GetInvisible()
#endif
};
foreach (var item in list)
{
#if ALL_VISIBLE
Method(item as dynamic); // only works if all types are visible (or with the compatibility fix)
#else
Method(item); // only works with the compatibility fix, probably better in that case
#endif
}
Console.ReadLine();
}
void Method<T>(Generic<T> instance)
{
Console.WriteLine(
"{0}: {1}",
instance.GetType().GenericTypeArguments[0].Name,
typeof(T).Name);
}
#if !ALL_VISIBLE
// all code below this line only for compatibility with invisible classes
DynamicDispatcher<Program> _dispatcher;
public Program()
{
_dispatcher = new DynamicDispatcher<Program, Generic<object>>(Method<object>);
}
void Method(Base instance)
{
Console.WriteLine("Resolving {0}...", instance.GetType());
_dispatcher.Dispatch(this, instance);
}
#endif
}
#if !ALL_VISIBLE
class DynamicDispatcher<TThis, TParam>
: DynamicDispatcher<TThis>
{
public DynamicDispatcher(Action<TParam> sampleAction)
: base(sampleAction.GetMethodInfo().GetGenericMethodDefinition()) { }
}
class DynamicDispatcher<TThis>
{
MethodInfo _target;
Dictionary<Type, Action<TThis, object>> _cache = new Dictionary<Type, Action<TThis, object>>();
public DynamicDispatcher(MethodInfo target)
{ _target = target; }
public void Dispatch(TThis @this, object instance)
{
Action<TThis, object> exactTarget;
var type = instance.GetType();
if (_cache.TryGetValue(type, out exactTarget) == false)
{
Console.WriteLine("Compiling dynamic method...");
DynamicMethod method = new DynamicMethod(
name: "Dynamic dispatch for " + type,
returnType: null,
parameterTypes: new[] { typeof(TThis), typeof(object) },
restrictedSkipVisibility: true);
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Castclass, instance.GetType());
il.Emit(OpCodes.Call, _target.MakeGenericMethod(instance.GetType().GetGenericArguments()));
il.Emit(OpCodes.Ret);
exactTarget = (Action<TThis, object>)method.CreateDelegate(typeof(Action<TThis, object>));
_cache.Add(type, exactTarget);
}
exactTarget(@this, instance);
}
}
#endif
}
// Output:
#if ALL_VISIBLE
/*
Int32: Int32
Boolean: Boolean
String: String
Base: Base
Generic`1: Generic`1
Object: Object
*/
#else
/*
Resolving DynamicDispatch.Generic`1[System.Int32]...
Compiling dynamic method...
Int32: Int32
Resolving DynamicDispatch.Generic`1[System.Boolean]...
Compiling dynamic method...
Boolean: Boolean
Resolving DynamicDispatch.Generic`1[System.String]...
Compiling dynamic method...
String: String
Resolving DynamicDispatch.Generic`1[DynamicDispatch.Base]...
Compiling dynamic method...
Base: Base
Resolving DynamicDispatch.Generic`1[DynamicDispatch.Generic`1[System.Int32]]...
Compiling dynamic method...
Generic`1: Generic`1
Resolving DynamicDispatch.Generic`1[System.Object]...
Compiling dynamic method...
Object: Object
Resolving DynamicDispatch.Generic`1[DynamicDispatch.Hiding+Invisible]...
Compiling dynamic method...
Invisible: Invisible
Resolving DynamicDispatch.Generic`1[DynamicDispatch.Hiding+Invisible]...
Invisible: Invisible
*/
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment