Обработка ошибок кастинга в рантайме предусмотрена, может, она представлена не всеми возможными случаями, но на базовом уровне присутствует, 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), ваш вариант базируется на конкретном контексте, в случае использование "абстрактного" Delegate будет осложнен прямой вызов и, тем более, кастинг передаваемых аргументов
С другой стороны, в вашем примере метод 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);
Если я не прав, то прошу меня поправить, в любом случае, ваш вариант мне очень понравился)
Почему же "молюсь": тип аргумента подставляется на завершающем этапе работы библиотеки, а представленный механизм позволяет унифициоровать нижестоящий слой, сведя все обработчики к единому виду без необходимости ручного прописывания типов делегатов; иными словами - оперирование конкретными типами всплывает наверх
Это что касается конкретно моего применения
С другой стороны, в чистом виде, мне показалось это весьма неплохим способом абстрактизации и интересным кейсом
Весьма благодарен за развернутый ответ и за красивое решение)
Обработка ошибок кастинга в рантайме предусмотрена, может, она представлена не всеми возможными случаями, но на базовом уровне присутствует, Exception будет выброшен, ниже пример
Я вам благодарен за репрезентативный пример)
Соглашусь, красивый вариант, но он также предполагает указание конкретного типа при конструировании результирующего делегата (см https://habr.com/ru/articles/738346/comments/#comment_25597142)
Как я писал в нижестоящем ответе (https://habr.com/ru/articles/738346/comments/#comment_25597142), ваш вариант базируется на конкретном контексте, в случае использование "абстрактного" Delegate будет осложнен прямой вызов и, тем более, кастинг передаваемых аргументов
Да, я немного косо сформулировал свое утверждения, я имел в виду обратное преобразование
А не проще тогда в случае вашего примера использовать максимально прострой код
С другой стороны, в вашем примере метод CastAction<T> предполагает явное указание типа аргумента, которое не потребуется в случае передачи Action<T>, использующее данный тип (контекста будет достаточно). В случае же, если будет осуществлена передача аргумента "абстрактного" типа (Delegate), то контекста для сборки уже не хватит
Если я не прав, то прошу меня поправить, в любом случае, ваш вариант мне очень понравился)
Увы, автор пробовал, но не запустилось, буду рад, если поделитесь примером кода или ссылкой на код)
Почему же "молюсь": тип аргумента подставляется на завершающем этапе работы библиотеки, а представленный механизм позволяет унифициоровать нижестоящий слой, сведя все обработчики к единому виду без необходимости ручного прописывания типов делегатов; иными словами - оперирование конкретными типами всплывает наверх
Это что касается конкретно моего применения
С другой стороны, в чистом виде, мне показалось это весьма неплохим способом абстрактизации и интересным кейсом
Проблема в скорости работы: по моим тестам, разница была примерно в 8 раз