C # /. NET Самый эффективный способ динамического вызова метода - PullRequest
0 голосов
/ 18 февраля 2019

Мы разрабатываем систему, которая читает команды из потока tcp / ip и затем выполняет эти команды.Команды состоят из вызова метода для объекта, также идентифицируемого 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.Это исключает время для обхода всех классов, методов и атрибутов, поскольку эта информация уже подготовлена.

Пожалуйста, воздержитесь от информирования меня о других узких местах, таких как сеть.Они не проблема.И они не являются причиной для использования медленных вызовов в другом месте кода.Спасибо.

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019

Может быть, вы хотите попробовать ObjectMethodExecutor

Согласно Hanselman :

Если вам когда-либо понадобится вызватьметод для типа через отражение, и этот метод может быть асинхронным, у нас есть помощник, который мы используем повсюду в базовом коде ASP.NET, который высоко оптимизирован и гибок и называется ObjectMethodExecutor.

Команда использует этокод в MVC для вызова методов вашего контроллера.Они используют этот код в SignalR для вызова ваших методов-концентраторов.Он обрабатывает асинхронные и синхронизирующие методы.Он также обрабатывает пользовательские ожидания и асинхронные рабочие процессы F #

0 голосов
/ 18 февраля 2019

Вы можете использовать открытые делегаты, которые примерно в десять раз быстрее, чем MethodInfo.Invoke.Вы могли бы создать такой delegate из MethodInfo, например:

delegate void OpenCommandCall(Callee element, parameters);

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

Затем вы бы назвали этого делегата следующим образом:

occDelegate.Invoke(callee, params);

Где callee - элемент, который выесли хотите вызвать метод, methodInfo - это MethodInfo метода, а parameters - это заполнитель для других параметров.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...