c# - Double dispatch inside a single inheritance tree -
i have inheritance tree of different line
-classes, starting abstract line
-class. want able intersect each line each other line, , sometimes, not know neither of runtime types, e.g. i'm calling line.intersect(line)
(so need double dispatch). call abstract overload of overriden intersect
-methods, e.g. circle.intersect(line)
instead of circle.intersect(actualtype)
. here's example code:
class program { static void main(string[] args) { line straightline = new straightline(); line circle = new circle(); // print: "circle intersecting line." // should print: "circle intersecting straight line." circle.intersect(straightline); console.readline(); } } abstract class line { public abstract void intersect(line line); public abstract void intersect(straightline straightline); public abstract void intersect(circle circle); } class straightline : line { public override void intersect(line line) { console.writeline("straigth line intersecting line."); } public override void intersect(straightline straightline) { console.writeline("straight line intersecting straight line."); } public override void intersect(circle circle) { console.writeline("straight line intersecting circle."); } } class circle : line { public override void intersect(line line) { console.writeline("circle intersecting line."); } public override void intersect(circle circle) { console.writeline("circle intersecting circle."); } public override void intersect(straightline straightline) { console.writeline("circle intersecting straight line."); } }
one possible workaround use dynamic
, do. however, want migrate .net standard-library, dynamic
not allowed.
are there other ways make work? i'd willing switch abstract class 1 or multiple interfaces, if helps. maybe visitor-pattern applicable, although i've seen used different inheritance trees (and find quite ugly).
it possible emulate double dispatch using reflection. targeting .net standard 1.1 , using nuget-package system.reflection
, intersect(line line)
-method not need abstract or virtual, has implemented once.
this whole example code .net standard library (i return string
instead of using console.writeline()
, since latter not available in .net standard):
using system.reflection; namespace intersectlibrary { public abstract class line { public string intersect(line line) { var method = this.gettype().getruntimemethod(nameof(intersect), new[] { line.gettype() }); return (string)method.invoke(this, new[] { line }); } public abstract string intersect(straightline straightline); public abstract string intersect(circle circle); } public class straightline : circle { public override string intersect(straightline straightline) { return "straight line intersecting straight line."; } public override string intersect(circle circle) { return "straight line intersecting circle."; } } public class circle : line { public override string intersect(circle circle) { return "circle intersecting circle."; } public override string intersect(straightline straightline) { return "circle intersecting straight line."; } } }
please note when targeting .net framework, system.reflection
offers different methods, , code need modified.
in console application, following happen:
using system; using intersectlibrary; namespace consoleapplication { class program { static void main(string[] args) { line straightline = new straightline(); line circle = new circle(); circle circle2 = new circle(); // calls "line.intersect(line)", , correctly // prints "circle intersecting straight line.". console.writeline(circle.intersect(straightline)); // calls "line.intersect(line)", // since argument's compile-time type "line". console.writeline(circle2.intersect(straightline)); // calls "line.intersect(circle)", // since argument's compile-time type "circle". // @ runtime, call resolved // "straightline.intersect(circle)" via single dispatch. console.writeline(straightline.intersect(circle2)); console.readline(); } } }
if have 1 object of compile-time type line
, 1 of concrete type (e.g. circle
), it's better call line.intersect(circle)
, since won't need (slower) reflection resolve method call. however, circle.intersect(line)
work, , importantly, calling line.intersect(line)
possible.
Comments
Post a Comment