Мы разрабатываем систему, которая читает команды из потока 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.
}
Этот код, должно быть, был инициализирован с некоторым тяжелым кодом отражения:
- Найдите все классы, которые могут представлять элемент.
- Найдите все методы, которые имеют атрибут команды и т. Д.
- Сохраните всю эту информацию в словаре, включая соответствующий
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
.Это исключает время для обхода всех классов, методов и атрибутов, поскольку эта информация уже подготовлена.
Пожалуйста, воздержитесь от информирования меня о других узких местах, таких как сеть.Они не проблема.И они не являются причиной для использования медленных вызовов в другом месте кода.Спасибо.