似乎我应该在这里拥有所需的一切,但是实现它的细节使我发疯。

我有一个静态实用程序方法,该方法采用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委托(delegate),但是我还没有想过将其与lambda函数结合使用,或者没有使接收器真正不在乎使用哪种特定的EventArgs风格。

我发现的唯一解决方法是创建一个通用模板参数,以传递特定的事件参数类型。这使我可以:
eventInfo.AddEventHandler(client, new EventHandler<E>(...));

其中E是相关参数。但是,这显然很笨拙,当提取的eventInfo应该告诉我们我们需要知道的所有信息时,必须将其传入似乎是错误的。

值得注意的是,我为Xamarin使用了一个受限制的PCL框架,该框架显然不包括我在相关问题中提到的静态Delegate.CreateDelegate()方法。我确实可以访问Activator,它应该涵盖大多数相同的基础。

最佳答案

事实证明,这并不是那么困难,但是确实需要一些代码并需要一点点反射(reflection)。

基本思想是将处理程序包装在由事件类型参数化的通用类中

HandlerFor<T> : IDisposable where T : EventArgs.

然后,此类具有与所需的事件处理程序签名匹配的私有(private)成员函数:
void Handle(object sender, T eventArgs)

它会在构造时注册,在处置时注销,并在事件发生时调用其构造函数中提供的给定Action

为了隐藏实现细节,并仅公开IDisposable作为受控事件处理程序生命周期范围和注销的句柄,我将其包装在EventHandlerRegistry类中。如果需要,这还将允许将来进行改进,例如构建工厂委托(delegate),而不是重复调用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();
    }
}

运行时,将给出以下输出:

10-07 16:39