Created
October 18, 2013 08:33
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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