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,并返回该值?这仍然与我尝试执行的操作无关。您已经描述过要等待事件发生。这是代码就是这样做的。如果你实际上不想等待事件发生,请你澄清一下。我正在尝试等待一个事件。是的,但我希望以一种可以抽象为助手类的方式来完成。