// Where does dynamic dispatch occur in C#? // Most of the examples here also applies to Java and Kotlin, with // minor syntactic differences. using System; public class A { public int x; public A(int y) {x=y;} // constructor public virtual void h() { Console.WriteLine("A.h"); } public void hb() { Console.WriteLine("A.hb"); } protected virtual int g() { return 10; } private int f() { return 1; } // private funs can't be virtual public static void s() { Console.WriteLine("A.s"); } public void f2() { Console.WriteLine("A.f2, f returns "+f()); } public void g2() { Console.WriteLine("A.g2, g returns "+g()); } }//class A class B : A // B extends A (only single inheritance of classes allowed) { public new int x = 999; // note 'new' is needed here to redefine x public B(int x0) : base(x0) {} // base calls superclass constructor public override void h() {Console.WriteLine("B.h"); } // note 'override' public new void hb() { Console.WriteLine("B.hb"); } // note 'new' private int f() { return 2; } // can't use 'new' or 'override' here protected override int g() { return 20; } // note 'override' public new static void s() { Console.WriteLine("B.s"); } // new public void q(string s) { Console.WriteLine("string version of q"); } public void q(object s) { Console.WriteLine("object version of q"); } }//class B public class inherit2 { public static void Main(string[] argv) { B m = new B(2); // static and dynamic types of m are the same (B) A n; // the static type of n is A if (argv.Length==0) n = new B(4); // but what's the dynamic type of n? else n = new A(1); n.h(); // dynamic dispatch here because of virtual-override n.hb(); // static dispatch, even though hb was re-written m.hb(); // now calls B.hb because static type of m is B (static dispatch) n.g2(); // dynamic dispatch works on protected methods n.f2(); // no dynamic dispatch on private methods (same as java) //n.s(); //not legal in C#, must call using class name: A.s() or B.s() // in java, n.s() would be legal, but uses static dispatch Console.WriteLine(n.x); // no dynamic dispatch on instance variables. // java has same retriction, but Kotlin instance vars can be 'open' Console.WriteLine(m.x); // now prints 999 object x = "abc"; // x has static type object, dynamic type string m.q(x); // which version of q did this call? answer is OBJECT version. // what can you conclude from this? }//main } /* What have we learned from this program? 1. ***Dynamic dispatch only occurs on 'this', That is, in m.q(x), dynamic dispatch only occurs on m, not on x. 'm' is 'this' while the function is being executed. In otherwords, method (and operator) overloading uses static type information to determine the right version of the function to call, and does not have the behavior of dynamic dispatch. THIS IS THE MOST IMPORTANT POINT HERE. 2. Dynamic dispatch occurs for public and protected (and internal) functions that are marked virtual/override. 3. Dynamic dispatch does not occur on private functions, static functions, instance variables, or public/protected/internal without virtual-override. In Java, all public and protected functions are virtual-override, while private functions and instances variables are not. In Kotlin, instance variables can also be marked 'open' and are subject to dynamic dispatch. These characteristics are language-specific and not nearly as important as point 1 above. 4. Functions and instance variables can be redefined with 'new', but only static type information is used to determine which function to call, which means that the function to call is determined at compile time. This point is C# specific. Of these points, no. 1 is the one that's misunderstood by a lot of people. So it's important for you to remember that dynamic dispatch does not occur on the arguments to a function, only on the object to the left-hand side of the little dot. This, ultimately, will show us why the oop paradigm is still limited. Why, you might ask, does it not use dynamic type information to disambiguate overloaded functions? Because in general there could be several variables: m.f(x,y,z): if each of its four arguments (including m) can be in one of three subclasses, then there are 3**4 = 81 different functions you must write for it to work. Dyanmic dispatch is confined to the first argument of f, which is m. */