Search code examples
c#inheritanceoverloadingvisitor-patterndouble-dispatch

Overloading method without modifying classes


I have access to a class structure, which I cannot modify, as follows:

Graphics
  Circle
  Line
  etc.

Again, I cannot modify it! These all have individual properties such as Radius, FirstPoint, LastPoint, etc., as well as some common properties.

I want to create a method that accepts a Graphics object, and depending on the type of object, will run a ToJson method:

Graphics g = db.GetGraphic(123);
// Console.WriteLine(g.GetType()) prints "Circle"

// Should run some specific implementation for `Circle` type graphics, and
// have an overload for all types including Graphics
ToJson(g);

At first I imagined I could craftily overload the ToJson method:

ToJson(Graphics g) { ... }
ToJson(Circle g) { ... }
ToJson(Line g) { ... }

however this of course goes for the generic ToJson(Graphics) overload each time.

I'm sure I could do something like the following:

if (g is Circle) ...
if (g is Line) ...
if (g is Graphics) ...

or create a Dictionary to reduce the complexity for each type, but that doesn't feel like the best way of doing things


What I've considered

I've considered if there's some generic wrapper method I could use around each object (e.g., new JsonGraphics(g).ToJson()), but I do not want to have to perform any manual type checking myself.

I have looked at the double dispatch and visitor pattern, but I wasn't sure they met my requirements as they look like I have to modify these classes (or maybe I just didn't fully understand them), and (kinda obvious, however) generics are also basically out of the window as they require me to know in advance what type of Graphics object this is.


Two questions then:

Is there a better way to do this other than to use some Dictionary or something other if (g is Type)-like?

If I could modify the classes, how would the pattern look? This isn't possible in this case, but is double dispatch/visitor the best way to go in the case I could?


Solution

  • Without being able to modify the base class, or have access to the concrete type before it's turned into a generic Graphics type, unfortunately I don't think there's anything you can do except inspect the runtime type of the Graphics object.

    You can use a switch statement (since C# 7.0), which is slightly cleaner than your if chain:

    switch (g)
    {
        case Circle circle: ... break;
        case Line line: ... break;
        default: /* Oh no! */ break;
    }
    

    Personally I don't see much advantage in using a Dictionary over a switch statement like this - both can be put away into a little self-contained method (and so reduce the amount of violation of the open/close principle), but the switch will be significantly cheaper.

    You can also use dynamic, which causes the runtime to do late binding:

    dynamic d = g;
    ToJson(d); // Picks the right ToJson overload corresponding to the runtime type of 'd'
    

    ... although, dynamic has a fairly large runtime cost, and is generally considered smell.