如何根据实际的运行时类型动态调用方法?

时间:2019-06-05 13:58:07

标签: c# .net architecture

我正在从外部资源(其中输入类型信息)接收JSON,并使用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); 

我还收到需要在对象上执行的命令类型(例如,发送,检索,删除等)。我用它来调用服务上的正确方法。该服务以这种格式为每种类型定义其方法(注意:我不维护该服务,我只是称呼它):

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

例如:

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

除了大的switch语句和每种类型的if-else块之外,还有其他更优雅的方法吗?这会起作用,但是看起来确实比较笨拙,如果我们继续添加类型,只会变得笨拙

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...
}

感谢您提供的任何见识

1 个答案:

答案 0 :(得分:0)

您可以使用relfection实现此目的。给定以下类别:

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;
    }
}

通过以下方式调用服务:

// 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 });

如果您根本不关心性能,建议不要使用这种方法,而只需使用开关即可。