Более быстрый вызов методов с использованием отражения - PullRequest
0 голосов
/ 05 мая 2020

Я переношу старый код с AS3 (через Haxe) на C#.
Части кода перенесены, другие я вручную переписал на C#. Одна из этих частей - диспетчеризация событий.

У меня есть слушатели событий, регистрирующиеся в диспетчере событий, у всех слушателей есть такая подпись:

public void handleSomething(Event e)
// they may also use a subclass of Event as a parameter
public void handleAnother(MouseEvent e)

События сохраняют небольшой объем данных и тип:

public class Event {

    public const string ENTER_FRAME = "enter_frame";
    public const string RESIZE = "resize";
    public const string CHANGE = "change";

    readonly string type;

    public Event(string type) {
        this.type = type;
    }
}

Я веду список с ключами для определенного типа события (строка по причинам устаревшего характера), после отправки события я нахожу соответствующие обработчики с ключами с этой строкой и вызываю их с данными события.

В настоящее время я использую для этого рефлексию, но она оказывается слишком медленной. Я нашел несколько потоков , которые разделяют эту проблему.

Моя особая проблема заключается в том, что сигнатура метода меняется, если бы она всегда была Event в качестве параметра, я мог бы использовать предоставленные решения, но, увы.

Я бы не отказался от обмена некоторой памяти / времени при настройке, чтобы последующие вызовы были быстрее. Я могу получить ссылку на метод и выяснить, какой тип он ожидает, но я не уверен, как сохранить и вызвать его позже?

1 Ответ

1 голос
/ 05 мая 2020

Вы можете создать и скомпилировать выражение LINQ для каждого метода обработчика и кэшировать его для использования в будущем.

public class CompiledDelegate
{
    // Assume that there is one one method per handler class type, add method name to dictionary key if necessary
    private static Dictionary<Type, CompiledDelegate> _Cache = new Dictionary<Type, CompiledDelegate>();

    public static CompiledDelegate Get(Type handlerType, string methodName)
    {
        CompiledDelegate result;
        if (!_Cache.TryGetValue(handlerType, out result))
        {
            var method = handlerType.GetMethod(methodName);

            // determine type of single method parameter
            var paramType = method.GetParameters().Single().ParameterType;

            // create expression tree (object h, object p) => ((handlerType)h).MethodName((paramType)p)
            var exprHandler = Expression.Parameter(typeof(object), "h");
            var exprParam = Expression.Parameter(typeof(object), "p");

            var lambda = Expression.Lambda(
                Expression.Call(
                    Expression.TypeAs(exprHandler, handlerType),  // instance, cast object to handlerType
                    method,                                       // method
                    Expression.TypeAs(exprParam, paramType)       // parameter, cast object to paramType
                ),
                exprHandler, exprParam                            // lamda params
            );

            result = new CompiledDelegate()
            {
                Method = method,
                // compile expression
                Compiled = (Action<object, object>)lambda.Compile()
            };

            _Cache.Add(handlerType, result);
        }

        return result;
    }

    public MethodInfo Method { get; private set; }
    public Action<object, object> Compiled { get; set; }
}

После того, как у вас есть экземпляр hander, вы можете вызвать его метод через скомпилированный делегат:

CompiledDelegate.Get(handler.GetType(), "handlerSomething").Compiled(handler, mouseEvent)

Вы можете предварительно сгенерировать CompiledDelegate для каждого из обработчиков и добавить их в таблицу отправки вместе с самими обработчиками.

Вызов метода через скомпилированный делегат (после его компиляции, конечно) примерно в 10 раз быстрее, чем вызов того же метода через отражение.

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