1

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?

Nick Bull
  • 9,518
  • 6
  • 36
  • 58
  • If you could modify the types, you would add a virtual/abstract ToJson instance method and override as needed. A "rule engine" where you use reflection to find the right implementation of the ToJson code is probably the best way to go. – Lasse V. Karlsen Mar 27 '19 at 14:42
  • So yes, a dictionary of type to delegate/object that implements this is probably what I would choose myself. – Lasse V. Karlsen Mar 27 '19 at 14:43
  • Decorator pattern? – Kenneth K. Mar 27 '19 at 14:43
  • Either double dispatch or visitor would serve the purpose, if it were not for the constraint that classes cannot be modified. – Tanveer Badar Mar 27 '19 at 14:43
  • If he were to use a decorator pattern he would need to know which decorator to decorate each object with, and though he would have specific ToJson code for specific objects, he would still need some way of identifying which decorator goes to which object. – Lasse V. Karlsen Mar 27 '19 at 14:44
  • @KennethK. Please note the constraint that I **can't modify the base classes** - they're part of a dll! – Nick Bull Mar 27 '19 at 14:45
  • @NickBull Yeah, that's the point of the decorator pattern. – Kenneth K. Mar 27 '19 at 14:45
  • @LasseVågsætherKarlsen Unfortunately I cannot as they're part of a dll that I cannot modify – Nick Bull Mar 27 '19 at 14:45
  • Since I'm a rather big fan of dependency injection, personally I would probably create a generic interface with the ToJson method, and register one concrete implementation for each such type of graphics object, and resolve the right one at runtime. – Lasse V. Karlsen Mar 27 '19 at 14:45
  • I understood that you can't modify them, which is why I think a dictionary of type->delegate/handler object is probably your best option. – Lasse V. Karlsen Mar 27 '19 at 14:46
  • @KennethK. How so? I thought each class would need to inherit some interface in the decorator pattern – Nick Bull Mar 27 '19 at 14:47
  • @LasseVågsætherKarlsen Okay, that's unfortunate! :( – Nick Bull Mar 27 '19 at 14:47
  • 1
    As you note, to use the visitor pattern for double dispatch, you need the "visited" class to implement a method that calls the "visitor" class back on the appropriate method. I give a simple example here: https://ericlippert.com/2015/05/04/wizards-and-warriors-part-three/ You normally use the visitor pattern when you have a group of related objects, you have a variety of analysis or transformation passes you need to perform on those objects, and the exact details of each operation depend on the run-time type of *both* the visited and visitor objects. – Eric Lippert Mar 27 '19 at 15:26
  • 1
    Thank you for your input Eric - I'll give that a read! – Nick Bull Mar 27 '19 at 17:07

1 Answers1

2

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.

canton7
  • 37,633
  • 3
  • 64
  • 77