C# 可重用类以等待触发事件

C# 可重用类以等待触发事件,c#,events,reflection,C#,Events,Reflection,我知道这里的问题有点重复: 然而,我正在写一封邮件,遇到了一个问题。以下是我一直在研究的(主要)简化版本: public class EventWaiter { private AutoResetEvent _autoResetEvent = new AutoResetEvent(false); private EventInfo _event = null; private object _eventContainer = null; public Event

我知道这里的问题有点重复:

然而,我正在写一封邮件,遇到了一个问题。以下是我一直在研究的(主要)简化版本:

public class EventWaiter
{
    private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
    private EventInfo _event = null;
    private object _eventContainer = null;

    public EventWaiter(object eventContainer, string eventName)
    {
        _eventContainer = eventContainer;
        _event = eventContainer.GetType().GetEvent(eventName);
    }
    public void WaitForEvent()
    {
        MethodInfo method = this.GetType().GetMethod("DynamicCaller");
        Delegate handler = Delegate.CreateDelegate(this._event.EventHandlerType, this, method);

        _event.AddEventHandler(_eventContainer, handler);

        _autoResetEvent.WaitOne();

        _event.RemoveEventHandler(_eventContainer, _handler);

    }
    public void DynamicCaller(/* insert magic here */)
    {
        _autoResetEvent.Set();
    }
}
其用法是:

EventWaiter ew = new EventWaiter(someClass, "someEvent");
ew.WaitForEvent();
基本上是将
DynamicCaller
void注册为此事件的处理程序。问题是,事件具有不同的签名,我希望能够处理事件,而不管使用的是哪个委托

我可以用这个来获取委托的类型。_event.EventHandlerType但是我如何使用它来创建一个完全可重用的类,不管委托是什么?如果DynamicCaller参数与事件委托参数不完全相同,则会出现异常


作为旁注,我对框架中的代码做了大量的研究,如果我能够访问其中的一些代码,我认为这将很容易。太糟糕了,我需要的很多类都是框架内部的。

由于所有与推荐模式相关的事件都有一个object类型的参数和一个派生自
EventArgs
的类型的参数,因此您应该能够使用此签名处理所有这些事件:

void DynamicCaller(object sender, EventArgs e)
当然,它不适用于非标准事件签名


编辑:下面是一个动态生成处理程序的示例:

public class EventWaiter
{
    private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
    private EventInfo _event = null;
    private object _eventContainer = null;

    public EventWaiter(object eventContainer, string eventName)
    {
        _eventContainer = eventContainer;
        _event = eventContainer.GetType().GetEvent(eventName);
    }
    public void WaitForEvent()
    {
        Delegate handler = CreateHandler();

        _event.AddEventHandler(_eventContainer, handler);

        _autoResetEvent.WaitOne();

        _event.RemoveEventHandler(_eventContainer, handler);

    }

    private Delegate CreateHandler()
    {
        var invokeMethod = _event.EventHandlerType.GetMethod("Invoke");
        var invokeParameters = invokeMethod.GetParameters();
        var handlerParameters = invokeParameters.Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();
        var body = Expression.Call(Expression.Constant(_autoResetEvent), "Set", null);
        var handlerExpression = Expression.Lambda(_event.EventHandlerType, body, handlerParameters);
        return handlerExpression.Compile();
    }
}

编辑:SLaks比我快;)

您应该使用表达式树编译一个方法,该方法包含一组调用回调函数的任意参数:

Expression.Lambda(
    _event.EventHandlerType,

    Expression.Call(Exrpession.Constant(_autoResetEvent), 
                    typeof(AutoResetEvent).GetMethod("Set")),

    _event.EventHandlerType.GetMethod("Invoke")
                           .GetParameters()
                           .Select(p => Expression.Parameter(p.ParameterType))
).Compile();
请注意,可以使用泛型和表达式树使系统类型安全:

 new EventWaiter(_ => someObject.SomeEvent += _)

其中,
\uuu
是一个普通(但较短)的参数名。

您可以使用
任务完成源代码执行任何操作:

  TaskCompletionSource<string> tcs = 
    new TaskCompletionSource<string>();

  WebClient client = new WebClient();

  client.DownloadStringCompleted += (sender, args) => {
    if (args.Error != null) tcs.SetException(args.Error);
    else if (args.Cancelled) tcs.SetCanceled();
    else tcs.SetResult(args.Result);
  };

  client.DownloadStringAsync(address);

  tcs.Task.Wait(); // WaitForEvent
TaskCompletionSource tcs=
新建TaskCompletionSource();
WebClient客户端=新的WebClient();
client.DownloadStringCompleted+=(发送方,参数)=>{
如果(args.Error!=null)tcs.SetException(args.Error);
如果(args.Cancelled)tcs.setcancelled();
else tcs.SetResult(args.Result);
};
client.DownloadStringAsync(地址);
tcs.Task.Wait();//等待出口

这里的解决方案很好,但对我来说,使用字符串时,反射有一点代码味道,所以我选择通用版本:

public class EventWaiter
{
    public enum Mode
    {
        Wait,
        Detach
    }

    public static Func<Mode, TEventArgs> Create<TDelegate, TEventArgs>(
        Func<Action<object, TEventArgs>, TDelegate> converter,
        Action<TDelegate> addHandler,
        Action<TDelegate> removeHandler
        )
    {
        AutoResetEvent semaphore = new AutoResetEvent(false);
        TEventArgs args = default(TEventArgs);

        TDelegate handler = converter((s, e) => {  args = e; semaphore.Set(); });

        addHandler(handler);

        return mode =>
        {
            if (mode == Mode.Wait)
            {
                semaphore.WaitOne(); 
                return args;
            }
            else
            {
                removeHandler(handler);
                return default(TEventArgs);
            }
        };
    }
公共类事件服务程序
{
公共枚举模式
{
等待
拆卸
}
公共静态函数创建(
Func转换器,
动作addHandler,
动作移除处理程序
)
{
AutoResetEvent信号量=新的AutoResetEvent(false);
TEventArgs args=默认值(TEventArgs);
TDelegate handler=converter((s,e)=>{args=e;semaphore.Set();});
addHandler(handler);
返回模式=>
{
if(mode==mode.Wait)
{
WaitOne()信号量;
返回args;
}
其他的
{
移除处理器(处理器);
返回默认值(TEventArgs);
}
};
}
用法:

        var evt = 
        EventWaiter.Create<SerialDataReceivedEventHandler, SerialDataReceivedEventArgs>
            (handler => (s, e) => handler(s, e), 
            h => port.DataReceived += h,
            h => port.DataReceived -= h);

        var firstArgument = evt(EventWaiter.Mode.Wait); //Wait for first event
        var secondArgument = evt(EventWaiter.Mode.Wait); //Wait for second event

        evt(EventWaiter.Mode.Detach); //Dispose
var-evt=
事件服务员,创建
(handler=>(s,e)=>handler(s,e),
h=>port.DataReceived+=h,
h=>port.DataReceived-=h);
var firstArgument=evt(eventwater.Mode.Wait);//等待第一个事件
var secondArgument=evt(eventwater.Mode.Wait);//等待第二个事件
evt(EventWaiter.Mode.Detach);//Dispose

我想你可能想看看TaskCompletionSource@PeterRitchie:我看过了,我看到了它的用处,但我看不出这与此有什么关系。它不能替代我的解决方案,也无助于解决我的问题。也许你可以提供一些更多的信息,说明你为什么建议使用TaskCompletionSource@PeterRitchie可能会有帮助:)我不明白你为什么不能为委托类型设置模板?是和否,尽管这是推荐的模式,而且所有的事情都应该遵循这一模式,但不是所有的事情都遵循这一模式,这就是问题的根源,因为我希望能够处理这种奇怪的情况。那么使用DynamicMethod并向其发送消息呢?@caesay、 是的,这就是我所说的“动态代码生成”(虽然你也可以使用Linq表达式)你能举个例子吗?@SLaks,传递lambda在这里不起作用,因为它不允许你检索EventInfo(至少不容易)@ThomasLevesque:正确。您需要遍历表达式树以获取实例和事件添加。@SLaks:是否有一种方法可以使用参数列表查看是否有基类型的EventArgs,并返回该值?这仍然与我尝试执行的操作无关。您已经描述过要等待事件发生。这是代码就是这样做的。如果你实际上不想等待事件发生,请你澄清一下。我正在尝试等待一个事件。是的,但我希望以一种可以抽象为助手类的方式来完成。