繁体   English   中英

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

[英]C#/.NET Most performant way to call a method dynamically

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

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

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

此代码必须使用一些反射重量代码进行初始化:

  1. 找到可能代表元素的所有类。
  2. 查找具有命令属性等的所有方法。
  3. 将所有这些信息存储在字典中,包括相应的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类的任何实例上调用该方法,并且不希望为每个可能的元素*命令保留委托的内存。 (会有很多元素。)

只是为了清楚说明: MethodInfo.Invoke比函数调用慢100倍,包括switch / case语句。 这排除了遍历所有类,方法和属性的时间,因为这些信息已经准备好了。

请不要告诉我有关网络等其他瓶颈的信息。 他们不是问题。 并且他们没有理由在代码中的另一个位置使用慢速调用。 谢谢。

您可以使用开放代理,它比MethodInfo.Invoke快十倍。 您可以从MethodInfo创建这样的delegate ,如下所示:

delegate void OpenCommandCall(Callee element, parameters);

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

然后,您可以将此委托称为:

occDelegate.Invoke(callee, params);

如果callee是要调用方法的元素,则methodInfo是方法的MethodInfoparameters是各种其他参数的占位符。

也许你想尝试一下ObjectMethodExecutor

汉塞尔曼说

如果你需要通过反射调用一个类型的方法,并且该方法可以是异步的,我们有一个帮助器,我们在ASP.NET核心代码库中的任何地方使用,它是高度优化和灵活的,称为ObjectMethodExecutor。

团队在MVC中使用此代码来调用您的控制器方法。 他们在SignalR中使用此代码来调用您的集线器方法。 它处理异步和同步方法。 它还处理自定义等待和F#异步工作流程

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM