Pull to refresh
1
0
Илья Кобрунов@DemiEljar

User

Send message

Весьма благодарен за развернутый ответ и за красивое решение)

Обработка ошибок кастинга в рантайме предусмотрена, может, она представлена не всеми возможными случаями, но на базовом уровне присутствует, Exception будет выброшен, ниже пример

/// <summary>
/// Реализованный метод вызова анонимного делегата действия
/// </summary>
/// <param name="args">Аргументы действия</param>
/// <returns></returns>
/// <exception cref="DelegateCastingErrorException"></exception>
public override object Invoke(params object[] args)
{
    try
    {
        (_Delegate as Action<T1>).Invoke((T1)args[0]);

        return null;
    }
    catch (Exception e)
    {
        throw new DelegateCastingErrorException(e.Message);
    }
}

Я вам благодарен за репрезентативный пример)

Соглашусь, красивый вариант, но он также предполагает указание конкретного типа при конструировании результирующего делегата (см https://habr.com/ru/articles/738346/comments/#comment_25597142)

Как я писал в нижестоящем ответе (https://habr.com/ru/articles/738346/comments/#comment_25597142), ваш вариант базируется на конкретном контексте, в случае использование "абстрактного" Delegate будет осложнен прямой вызов и, тем более, кастинг передаваемых аргументов

Да, я немного косо сформулировал свое утверждения, я имел в виду обратное преобразование

А не проще тогда в случае вашего примера использовать максимально прострой код

static Action<object> CastAction<T>(Action<T> action)
{
    return delegate(object value)
    {
        action?.Invoke((T)value);
    };
}

С другой стороны, в вашем примере метод CastAction<T> предполагает явное указание типа аргумента, которое не потребуется в случае передачи Action<T>, использующее данный тип (контекста будет достаточно). В случае же, если будет осуществлена передача аргумента "абстрактного" типа (Delegate), то контекста для сборки уже не хватит

// Это ваш код
static Action<object> CastActionExpression<T>(Action<T> action)
{
    // Строим выражение: (act, obj) => act.Invoke((T)obj)
    var actParamExpr = Expression.Parameter(typeof(Action<T>));
    var objParamExpr = Expression.Parameter(typeof(object));
    var convertExpr = Expression.Convert(objParamExpr, typeof(T));
    var mi = typeof(Action<T>).GetMethod("Invoke")!;
    var invokeExpr = Expression.Call(actParamExpr, mi, convertExpr);

    var lambda = Expression.Lambda<Action<Action<T>, object>>(
        invokeExpr, actParamExpr, objParamExpr);

    var compiled = lambda.Compile();
    return obj => compiled(action, obj);
}
// Эмуляция получения делегата из внешнего источника
static Delegate GetDelegate()
{
    Action<int> castingDelegate = delegate (int value)
    {
        Console.WriteLine(value);
    };

    return (Delegate)castingDelegate;
}

// Получение объекта делегата
var testDelegate = GetDelegate();

// Тут ошибка с требованием явно указать тип
testDelegate = CastActionExpression(testDelegate);

Если я не прав, то прошу меня поправить, в любом случае, ваш вариант мне очень понравился)

Увы, автор пробовал, но не запустилось, буду рад, если поделитесь примером кода или ссылкой на код)

Почему же "молюсь": тип аргумента подставляется на завершающем этапе работы библиотеки, а представленный механизм позволяет унифициоровать нижестоящий слой, сведя все обработчики к единому виду без необходимости ручного прописывания типов делегатов; иными словами - оперирование конкретными типами всплывает наверх

Это что касается конкретно моего применения

С другой стороны, в чистом виде, мне показалось это весьма неплохим способом абстрактизации и интересным кейсом

Проблема в скорости работы: по моим тестам, разница была примерно в 8 раз

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Registered
Activity