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