C#/.NET动态调用方法的最高效方式

我们正在开发一个系统,该系统将从tcp / ip流中读取命令,然后执行这些命令.命令由对对象的方法调用组成,该对象也由命令中的id标识.您可以将命令视为元素ID(寻址我们要在其上调用命令的元素)和命令ID(寻址应在元素上调用的方法)的信息.另外,还有一个问题,我们需要检查每个命令的某种权限以及如何执行此命令. (是否应在新线程中启动,等等.)

这样的命令调用的示例如下所示:

class Callee
{
    public void RegularCall(int command, parameters)
    {
        switch (command)
        {
            case 1: // Comand #1
                // Check if the permissions allow this command to be called.
                // Check if it should be outsourced to the ThreadPool and
                // call it accordingly. +Other Checks.
                // Finally execute command #1.
                break;
            case 2: // Comand #2
                // Check if the permissions allow that command to be called.
                // Check if it should be outsourced to the ThreadPool and
                // call it accordingly. +Other Checks.
                // Finally execute command #2.
                break;
            // Many more cases with various combinations of permissions and
            // Other flags.
        }
    }
}

在某处:

static Dictionary<int, Callee> callees = new Dictionary<int, Callee>();

static void CallMethod(int elementId, int commandId, parameters)
{
    callees[elementId].RegularCall(commandId, parameters);
}

但是,此方法有些不确定:

>由于一遍又一遍地复制相同的代码,这可能容易出错.
>在某些情况下,很难看到哪些命令存在以及它们的标志是什么.
>命令方法充满了方法外可能进行的检查.

我的第一种方法是使用反射,看起来应该是这样的:

class Callee
{
    [Command(1)]
    [Permissions(0b00111000)]
    [UseThreadPool]
    public void SpeakingNameForCommand1(parameters)
    {
        // Code for command #1.
    }

    [Command(2)]
    [Permissions(0b00101011)]
    public void SpeakingNameForCommand2(parameters)
    {
        // Code for command #2.
    }

    // Again, many more commands.
}

此代码必须已使用一些反射较重的代码初始化:

>查找所有可能代表一个元素的类.
>查找所有具有命令属性的方法,等等.
>将所有这些信息存储在字典中,包括相应的MethodInfo.

收到的命令的调用如下所示,其中CommandInfo是一个类,其中包含调用所需的所有信息(MethodInfo,在ThreadPool中运行,权限…):

static Dictionary<int, CommandInfo> commands = new Dictionary<int, CommandInfo>();

static void CallMethod(int elementId, int commandId)
{
    CommandInfo ci = commands[commandId];

    if (ci.Permissions != EVERYTHING_OK)
        throw ...;

    if (ci.UseThreadPool)
        ThreadPool.Queue...(delegate { ci.MethodInfo.Invoke(callees[elementId], params); });
    else
        ci.MethodInfo.Invoke(callees[elementId], params);
}

当我进行微基准测试时,对MethodInfo.Invoke的调用比直接调用慢大约100倍.问题是:是否有一种更快的方法来调用这些“命令”方法,而又不失去定义这些命令应如何调用的属性的优美性?

我也尝试从MethodInfo派生一个委托.但是,这不能很好地工作,因为我需要能够在Callee类的任何实例上调用该方法,并且不想为每个可能的element *命令为委托保留内存. (将有很多元素.)

为了清楚起见:MethodInfo.Invoke比包含switch / case语句的函数调用慢100倍.这不花时间遍历所有类,方法和属性,因为这些信息已经准备好了.

请不要让我知道其他瓶颈,例如网络.他们不是问题.而且,他们没有理由在代码的另一个位置使用慢速调用.谢谢.

解决方法:

您可以使用开放委托,它比MethodInfo.Invoke快大约十倍.您可以通过下面的MethodInfo创建这样的委托:

delegate void OpenCommandCall(Callee element, parameters);

OpenCommandCall occDelegate = (OpenCommandCall)Delegate.CreateDelegate(typeof(OpenCommandCall), methodInfo));

然后,您可以这样称呼此代表:

occDelegate.Invoke(callee, params);

其中被调用者是您要调用该方法的元素,其中methodInfo是该方法的MethodInfo,而参数是各种其他参数的占位符.

上一篇:前端的内存监控与错误监控


下一篇:反向时间迁移降低算力