C# 如何使用lambda创建新的EventHandler?

C# 如何使用lambda创建新的EventHandler?,c#,wcf,xamarin,C#,Wcf,Xamarin,看起来我应该在这里拥有我所需要的一切,但是实现它的细节让我发疯 我有一个静态实用程序方法,它接受一个web服务客户机对象,从中提取一个指定的EventInfo,并向该事件添加一些处理程序,基本上是在web服务调用完成时回调。问题是,由于任何给定的事件本质上都有自己的处理程序重载(WCF生成的代码为每个方法和相应的事件提供了唯一的SomeMethodCompletedEventArgs),因此我不知道如何实现这一点 我有两个要附加的处理程序,第一个只是lambda函数: (obj, args) =

看起来我应该在这里拥有我所需要的一切,但是实现它的细节让我发疯

我有一个静态实用程序方法,它接受一个web服务客户机对象,从中提取一个指定的EventInfo,并向该事件添加一些处理程序,基本上是在web服务调用完成时回调。问题是,由于任何给定的事件本质上都有自己的处理程序重载(WCF生成的代码为每个方法和相应的事件提供了唯一的
SomeMethodCompletedEventArgs
),因此我不知道如何实现这一点

我有两个要附加的处理程序,第一个只是lambda函数:

(obj, args) => task.Complete()
所以我想做的就是这么简单:

eventInfo.AddEventHandler(client, new EventHandler((obj, args) => task.Complete()));
但是,这会生成运行时InvalidCastException,因为eventInfo需要的是
EventHandler
,而不是普通的
EventHandler
。我相信这意味着我需要以某种方式使用
eventInfo.EventHandlerType
动态创建
EventHandler
委托,但我还没有想到如何将其与lambda函数相结合,或者以其他方式创建一个真正不关心使用的
EventArgs
的特定风格的接收器

我发现的唯一解决方法是创建一个通用模板参数,用于传递特定的事件参数类型。这使我能够:

eventInfo.AddEventHandler(client, new EventHandler<E>(...));
eventInfo.AddEventHandler(客户端,新的EventHandler(…);
其中
E
是有问题的参数。然而,这显然很笨拙,而且当提取的
eventInfo
应该告诉我们需要知道的所有信息时,必须将其传递进来似乎是错误的


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

在您提供的示例中,您应该能够删除显式委托构造函数:

eventInfo.AddEventHandler(client, (obj, args) => task.Complete());
通过让C#为您推断委托类型,它应该创建参数所需的正确委托类型

如果这不能解决您的具体问题,请提供可靠的重现问题的方法,并清楚、准确地解释上述方法没有帮助的原因


另一方面,很难从您发布的代码中看出什么,但是使用显式的
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());
您可以将上面的内容作为一种扩展方法来编写,以简化调用(您没有说明
客户机
是什么类型,所以我只是将其作为示例使用
对象
):

用法示例:

eventInfo.AddEventHandler(client, (obj, args) => task.Complete());
如果您担心API自己的
AddEventHandler()
方法在某个时候可能会改变,从而导致C#编译器选择其实现而不是扩展方法,或者如果它今天选择了扩展方法,那么请给扩展方法取一个不同的名称(假设只有一个
AddEventHandler())
方法重载,第二个参数为
委托
,但是,由于缺少一个好的、最少的、完整的代码示例,我不能保证它在您自己的代码中工作;使用唯一的名称可以保证它会工作,但代价是暴露出一点“魔力”:)


最后,在确定了一个可行的解决方案后,我能够搜索Stack Overflow以查找类似的问题,并找到了这个问题,您可以认为您自己的问题是重复的:


我决定在这里编辑我自己的答案,而不仅仅是建议结束你的问题,因为另一个问题的答案并没有提供我认为的优雅或易于使用的示例。

事实证明,这并不难,但确实需要一些代码和一些反思

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

HandlerFor<T> : IDisposable where T : EventArgs. 
它将在构造时注册,在处置时取消注册,并在事件发生时调用构造函数中提供给它的给定
操作

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

看起来是这样的:

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();
        }
    }
}
运行时,它将给出以下输出:


请注意,通常不建议将lambda附加到事件处理程序,因为它们只能通过相当复杂的反射过程删除。@OblivousSage:“它们只能通过相当复杂的反射过程删除”——这是错误的。首先,在许多情况下,永远不需要删除事件处理程序。其次,在需要的场景中,只需保留委托实例refe
void Handle(object sender, T eventArgs) 
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();
        }
    }
}
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();
    }
}