似乎就像我应该拥有我需要的一切,但实现它的细节让我发疯。
我有一个静态实用程序方法,它接受一个Web服务客户端对象,从中提取一个指定的EventInfo,并且应该为该事件添加一些处理程序,实质上是Web服务调用完成时的回调。问题是,因为任何给定的事件本身都有自己的处理程序重载(WCF生成的代码为每个方法和相应的事件提供了唯一的SomeMethodCompletedEventArgs
),我无法弄清楚如何实现这一点。
我有两个我想要附加的处理程序,第一个只是一个lambda函数:
(obj, args) => task.Complete()
所以我想做的就是这么简单:
eventInfo.AddEventHandler(client, new EventHandler((obj, args) => task.Complete()));
但是,这会生成运行时InvalidCastException,因为eventInfo需要EventHandler<SomeMethodCompletedEventArgs>
,而不是普通的EventHandler
。我相信这意味着我需要以某种方式使用EventHandler
动态创建eventInfo.EventHandlerType
委托,但我还没想出要将它与lambda函数结合起来,或者制作一个真正没有护理正在使用EventArgs
的特定风格。
我发现的唯一解决方法是创建一个通用模板参数,用于传递特定的事件参数类型。这使我能够:
eventInfo.AddEventHandler(client, new EventHandler<E>(...));
E
是有问题的参数。然而,这显然是笨拙的,当提取的eventInfo
告诉我们所有我们需要知道的事情时,必须通过它似乎是错误的。
值得注意的是,我使用了一个稍微受限制的Xamarin PCL框架,它显然不包括我在相关问题中提到的静态Delegate.CreateDelegate()
方法。我做可以访问Activator
,但这应该涵盖大部分相同的基础。
答案 0 :(得分:2)
在您提供的示例中,您应该只能删除显式委托构造函数:
eventInfo.AddEventHandler(client, (obj, args) => task.Complete());
通过让C#为您推断委托类型,它应该创建参数所需的正确委托类型。
如果这不能解决您的具体问题,请提供可靠地再现问题的a good, minimal, complete code example,以及对上述方法无效帮助的明确,准确的解释。
顺便说一句,很难说你发布的代码很少,但是有一个明确的AddEventHandler()
方法是不寻常的。通常,类只会公开一个事件,您可以使用+=
语法来订阅事件处理程序。
修改强>
根据您的评论,我了解API要求您遵守动态提供的事件签名。就个人而言,我认为这种设计很傻,但我认为你坚持使用它,可能是因为Xamarin框架的设计。
严格遵守规定的目标 - 也就是说,给定一个EventHandler
实例,生成一个新的委托实例,其类型与运行时提供的Type
实例匹配 - 以下方法应该有效为你:
static Delegate CreateDelegate(Type type, EventHandler handler)
{
return (Delegate)type.GetConstructor(new [] { typeof(object), typeof(IntPtr) })
.Invoke(new [] { handler.Target, handler.Method.MethodHandle.GetFunctionPointer() });
}
使用示例:
eventInfo.AddEventHandler(client,
CreateDelegate(eventInfo.EventHandlerType, (obj, args) => task.Complete());
您可以将上面的内容编写为扩展方法来简化调用(您没有说明client
是什么类型,所以我只是为了示例而设置了object
:
public static void AddEventHandler(this EventInfo eventInfo, object client, EventHandler handler)
{
object eventInfoHandler = eventInfo.EventHandlerType
.GetConstructor(new[] { typeof(object), typeof(IntPtr) })
.Invoke(new[] { handler.Target, handler.Method.MethodHandle.GetFunctionPointer() });
eventInfo.AddEventHandler(client, (Delegate)eventInfoHandler);
}
使用示例:
eventInfo.AddEventHandler(client, (obj, args) => task.Complete());
如果您担心在某些时候API自己的AddEventHandler()
方法可能会导致C#编译器选择其实现而不是扩展方法,或者当然如果它今天这样做(以上只会假设一个AddEventHandler()
方法重载,第二个参数为Delegate
,但是再次......缺少一个好的,最小,完整的代码示例我不能保证它将在您自己的代码中工作;使用唯一的名称将保证它将以暴露一些“魔法”:)的成本。)。
最后,在确定了一个可行的解决方案之后,我能够搜索Stack Overflow以寻找类似的问题,并找到了这个问题,你可以认为你自己的问题是重复的:Using reflection to specify the type of a delegate (to attach to an event)?
我决定继续在这里编辑我自己的答案,而不仅仅是建议关闭你的问题,因为对另一个问题的回答并没有提供我认为优雅或易于使用的例子。
答案 1 :(得分:1)
事实证明这并不是那么困难,但确实需要一些反射的代码。
基本思想是将处理程序包装在由事件类型
参数化的泛型类中HandlerFor<T> : IDisposable where T : EventArgs.
此类具有与所需事件处理程序签名匹配的私有成员函数:
void Handle(object sender, T eventArgs)
它将在构造上注册,在注销时取消注册,并且每当事件发生时将调用在其构造函数中提供给它的给定Action
。
为了隐藏实现细节,并仅公开IDisposable
作为受控事件处理程序生命周期范围和取消注册的句柄,我将其包装在EventHandlerRegistry
类中。如果需要,这还可以在将来添加改进,例如构建工厂委托,而不是重复调用Activator.CreateInstance()
来构建HandleFor
实例。
看起来像这样:
public class EventHandlerRegistry : IDisposable
{
private ConcurrentDictionary<Type, List<IDisposable>> _registrations;
public EventHandlerRegistry()
{
_registrations = new ConcurrentDictionary<Type, List<IDisposable>>();
}
public void RegisterHandlerFor(object target, EventInfo evtInfo, Action eventHandler)
{
var evtType = evtInfo.EventHandlerType.GetGenericArguments()[0];
_registrations.AddOrUpdate(
evtType,
(t) => new List<IDisposable>() { ConstructHandler(target, evtType, evtInfo, eventHandler) },
(t, l) => { l.Add(ConstructHandler(target, evtType, evtInfo, eventHandler)); return l; });
}
public IDisposable CreateUnregisteredHandlerFor(object target, EventInfo evtInfo, Action eventHandler)
{
var evtType = evtInfo.EventHandlerType.GetGenericArguments()[0];
return ConstructHandler(target, evtType, evtInfo, eventHandler);
}
public void Dispose()
{
var regs = Interlocked.Exchange(ref _registrations, null);
if (regs != null)
{
foreach (var reg in regs.SelectMany(r => r.Value))
reg.Dispose();
}
}
private IDisposable ConstructHandler(object target, Type evtType, EventInfo evtInfo, Action eventHandler)
{
var handlerType = typeof(HandlerFor<>).MakeGenericType(evtType);
return Activator.CreateInstance(handlerType, target, evtInfo, eventHandler) as IDisposable;
}
private class HandlerFor<T> : IDisposable where T : EventArgs
{
private readonly Action _eventAction;
private readonly EventInfo _evtInfo;
private readonly object _target;
private EventHandler<T> _registeredHandler;
public HandlerFor(object target, EventInfo evtInfo, Action eventAction)
{
_eventAction = eventAction;
_evtInfo = evtInfo;
_target = target;
_registeredHandler = new EventHandler<T>(this.Handle);
_evtInfo.AddEventHandler(target, _registeredHandler);
}
public void Unregister()
{
var registered = Interlocked.Exchange(ref _registeredHandler, null);
if (registered != null)
// Unregistration is awkward:
// doing `RemoveEventHandler(_target, registered);` won't work.
_evtInfo.RemoveEventHandler(_target, new EventHandler<T>(this.Handle));
}
private void Handle(object sender, T EventArgs)
{
if (_eventAction != null)
_eventAction();
}
public void Dispose()
{
Unregister();
}
}
}
它支持以非常透明的方式清理添加和删除事件处理程序。注意:这还没有以推荐的方式实现IDisposable
。您必须自己添加终结器和Dispose(bool isFinalizing)
。
这显示了它的用法示例:
public class MyArgs1 : EventArgs
{
public string Value1;
}
public class MyEventSource
{
public event EventHandler<MyArgs1> Args1Event;
public EventInfo GetEventInfo()
{
return this.GetType().GetEvent("Args1Event");
}
public void FireOne()
{
if (Args1Event != null)
Args1Event(this, new MyArgs1() { Value1 = "Bla " });
}
}
class Program
{
public static void Main(params string[] args)
{
var myEventSource = new MyEventSource();
using (var handlerRegistry = new EventHandlerRegistry())
{
handlerRegistry.RegisterHandlerFor(
myEventSource,
myEventSource.GetEventInfo(),
() => Console.WriteLine("Yo there's some kinda event goin on"));
handlerRegistry.RegisterHandlerFor(
myEventSource,
myEventSource.GetEventInfo(),
() => Console.WriteLine("Yeah dawg let's check it out"));
myEventSource.FireOne();
}
myEventSource.FireOne();
}
}
运行时,它会给出以下输出: