using System; using System.Collections.Generic; using System.Reflection; namespace DynamicDispatch { // A base class that establishes basic functionality and an API that we // would like to use with sub-classes. class MyBase { // Here's where we cache the methods associated with each pair of types // that's implemented in this assembly. private static Dictionary dispatch; // Build the multiple dispatch table by reflecting over all the types // defined in this assembly. static MyBase() { dispatch = new Dictionary(); foreach(Type t in Assembly.GetCallingAssembly().GetTypes()) { if(t.IsSubclassOf(typeof(MyBase))) { foreach(MethodInfo mi in t.GetMethods()) { if(mi.Name == "Handle") { ParameterInfo[] pars = mi.GetParameters(); if(pars.Length == 1) { Int64 code = ((Int64)t.GetHashCode() << 32) + pars[0].ParameterType.GetHashCode(); dispatch.Add(code, mi); } } } } } } // This is the magic method that we want to overload based on runtime types. public virtual void Handle(object o) { Int64 hash = ((Int64)this.GetType().GetHashCode() << 32) + o.GetType().GetHashCode(); if(dispatch.ContainsKey(hash)) { dispatch[hash].Invoke(this, new object[] {o}); } else { // This is our fallback functionality Console.WriteLine("Handling object " + o.ToString()); } } } class MySub1 : MyBase { public void Handle(float f) { Console.WriteLine("Handling float " + f.ToString()); } } class MySub2 : MyBase { public void Handle(string s) { Console.WriteLine("Handling string " + s); } public void Handle(float f) { Console.WriteLine("Handling float in a second manner " + (f*2.0).ToString("f1")); } } class Program { public static void Main() { Console.WriteLine("Let's play with method dispatch..."); MyBase b = new MySub1(); MyBase c = new MySub2(); b.Handle(3); b.Handle(2.1f); c.Handle(2.1f); c.Handle("Hi, there!"); } } }