0

I am receiving JSON from an outside source (with type info in it) that I deserialize with JSON.NET

JsonSerializerSettings settings = new JsonSerializerSettings(); 
settings.TypeNameHandling = TypeNameHandling.All;
//because the type info is included 'obj' will be of type Foo, Bar, Baz, etc
var obj = JsonConvert.DeserializeObject(jsonString, settings); 

I also receive the type of command that I need to execute on the object (e.g. Send, Retrieve, Remove, etc). I use this to invoke the correct method on a service. The service defines its methods for each type in this format (note: I don't maintain the service, I just call it):

///where T has an implementation for each type (Foo, Bar, etc)
T Send(T objToSend)
T Retrieve (T objToRet)
T Remove (T objToRemove)

so for example:

Foo f = service.Send(aFoo);
Bar b = service.Send(aBar);
Foo f2 = service.Retrieve(aFoo);
Bar b2 = service.Retrieve(aBar);

is there a more elegant way to do this other than a big switch statement and if-else blocks for every type? This will work, but it seems really clunky and if we continue to add types it will only get clunkier

switch(command){
    case "Send":
        if(obj is Foo){
            service.Send((Foo)obj);
        }
        if(obj is Bar){
            service.Send((Bar)obj);
        }
        if(obj is Baz){
            service.Send((Baz)obj);
        }
    break;
    case "Retrieve":
    //etc...
    case "Remove":
    //etc...
}

thank you for any insight you can provide

snappymcsnap
  • 2,050
  • 2
  • 29
  • 53
  • 1
    I guess `dynamic` would work, but it comes with some trade offs, of course. – sloth Jun 05 '19 at 14:07
  • Can you modify `Foo`, `Bar`? `Send` routing can be done with interface. And for routing command types you still need `switch/case`, `if` or something with keys (e.g. dictionary). – Sinatr Jun 05 '19 at 14:08
  • [Related](https://stackoverflow.com/q/35628735/1997232). – Sinatr Jun 05 '19 at 14:10
  • If you can use an Interface, like Sinatr suggested, that would be the most elegant way. Otherwise, you could use Reflections for it. – Nikolaus Jun 05 '19 at 14:12
  • Is there anywhere else in your code where you need a reference to `Foo` to be typed as `Foo` and a reference to `Bar` to be typed as `Bar`? The runtime overload resolution of `dynamic` is tempting, but I'd probably just do this with reflection. – 15ee8f99-57ff-4f92-890c-b56153 Jun 05 '19 at 14:14
  • You could make a generic Send and rely on runtime binding – Davesoft Jun 05 '19 at 14:34

1 Answers1

0

You could use relfection to achieve this. Given the following classes:

public class Bar
{
}

public class Foo
{
}

public class Service
{
    public Bar Send(Bar objToSend)
    {
        Console.WriteLine("Send Bar");
        return objToSend;
    }

    public Foo Send(Foo objToSend)
    {
        Console.WriteLine("Send Foo");
        return objToSend;
    }
}

Call the service in the following way:

// This is the object we received and need to process
var obj = new Bar();

// This is the method we need to execute
string method = "Send";

// Initialize a new service and get its type
var service = new Service();
Type serviceType = service.GetType();

// Get the correct method by looking at the method name and the parameter type
MethodInfo methodInfo = serviceType.GetMethods().FirstOrDefault(x => x.Name == method && x.GetParameters()[0].ParameterType == obj.GetType());

// Invoke the method
object returnObj = methodInfo.Invoke(service, new object[] { obj });

If you care about performance at all, I recommend against this approach, and simply use a switch instead.

Bart van der Drift
  • 1,287
  • 12
  • 30