Динамически создаваемый обработчик событий, который записывает его в параметр ref.C#

Место общения программистов C#
Ответить
Anonymous
 Динамически создаваемый обработчик событий, который записывает его в параметр ref.

Сообщение Anonymous »

Я хочу подписаться на любое событие, существующее в данном объекте, тип которого заранее неизвестен. Я создаю обработчик событий во время выполнения, используя System.Linq.Expressions.Expression.
Код, который у меня есть (см. ниже), почти работает... Но, к сожалению, есть некоторые события, которые не следуют шаблону отправителя, EventArgs, и я не могу это контролировать. Некоторые из них имеют параметры ref, и попытка записать их обратно (что, к сожалению, мне нужно сделать) вызывает System.ArgumentException: 'Невозможно привязаться к целевому методу, поскольку его подпись или прозрачность безопасности несовместимы с подписью метода тип делегата.' в Lambda.Compile(). Альтернативой было бы переписать его для генерации IL, но я бы предпочел этого избежать.
Событие:
public delegate void SomeEventDelegate(ref int a, int b);
public event SomeEventDelegate SomeEvent;

Динамически создаваемая лямбда-выражение:
(ref int a, int b) =>
{
var __values__ = new[] { a, b };

Action.Invoke("SomeEvent", new[] { "a", "b" }, __values__);
a = (int)__values__[0]; //this causes an exception in `lambda.Compile()`
}

Полный пример:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

using AgileObjects.ReadableExpressions; //optional, converts generated expression to more readable string

class PseudoControl
{
public delegate void SomeEventDelegate(ref int a, int b);
public event SomeEventDelegate SomeEvent;

public void RaiseEvents()
{
var a = 0;
var b = 0;
SomeEvent?.Invoke(ref a, b);
}
}

public class ControlExtender
{
public object ExtendedControl { get; set; }

public void SubscribeToAllEvents()
{
foreach (var ei in ExtendedControl.GetType().GetEvents(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public))
{
SubscribeToEvent(ei);
}
}

private void SubscribeToEvent(EventInfo ei)
{
var handlerType = ei.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();

//lambda: (p1, p2, ...) => RaiseObjectEvent(ei.Name, new[] { pname1, pname2, ... }, new[] { pval1, pval2, ...});
var parameters = eventParams
.Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();

var action = new Action(RaiseObjectEvent);
var constEventName = Expression.Constant(ei.Name);
var arrPNames = Expression.NewArrayInit(typeof(string), parameters.Select(p => Expression.Constant(p.Name)));
var arrPValues = Expression.NewArrayInit(typeof(object), parameters.Select(p => Expression.Convert(p, typeof(object))));

var statements = new List();
var varPValues = Expression.Variable(typeof(object[]), "__values__");
statements.Add(Expression.Assign(varPValues, arrPValues));
statements.Add(Expression.Call(Expression.Constant(action), action.GetType().GetMethod("Invoke"), constEventName, arrPNames, varPValues));

//handle `ref` parameters being written into
for (int i = 0; i < parameters.Length; i++)
{
var p = parameters;
if (p.IsByRef)
{
var value = Expression.ArrayAccess(varPValues, Expression.Constant(i));
var unbox = Expression.Convert(value, p.Type);
var assign = Expression.Assign(p, unbox); // { a = 1; b = 2; };
control.RaiseEvents();
}
}


Подробнее здесь: https://stackoverflow.com/questions/790 ... -parameter
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»