c# - 如何使用 lambda 来创建一个新的 EventHandler?

标签 c# wcf xamarin

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

我有一个静态实用方法,它接受一个 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 , 不过,这应该涵盖大部分相同的基础。

最佳答案

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

eventInfo.AddEventHandler(client, (obj, args) => task.Complete());

通过让 C# 为您推断委托(delegate)类型,它应该准确地创建参数所需的正确委托(delegate)类型。

如果这不能解决您的具体问题,请提供 a good, minimal, complete code example可靠地重现问题,并清楚、准确地解释为什么上述方法不起作用。


顺便说一句,很难从您发布的小代码中判断出来,但是有一个明确的 AddEventHandler() 方法是不寻常的。通常,一个类只会公开一个事件,您可以使用 += 语法来订阅一个事件处理程序。


编辑:

根据您的评论,我了解到 API 要求您遵守动态提供的事件签名。就我个人而言,我认为这种设计很愚蠢,但我假设您坚持使用它,大概是由于 Xamarin 框架的设计。

严格按照既定目标——即,给定一个 EventHandler 实例,生成一个新的委托(delegate)实例,其类型与运行时提供的 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)?

我决定继续在这里编辑我自己的答案,而不是仅仅提议关闭您的问题,因为对另一个问题的答案并没有真正提供我认为优雅或易于使用的示例。

关于c# - 如何使用 lambda 来创建一个新的 EventHandler?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29655381/

相关文章:

c# - C# 中的 Java 泛型

javascript - 在 Visual Studio 托管服务上托管 HTML/CSS 网站

.net - 添加服务引用时出错 : Type is a recursive collection data contract which is not supported

c# - SSL/TLS 中的相互认证

c# - Xamarin Android 中选项卡更改前的验证

c# - Marshal.Copy 的 Objective-C 等价物是什么

c# - 任何自动克隆数据对象的方法?

c# - 如何避免重复的xaml代码

wcf - SSL 服务器证书存在 WCF 的一个名称?

c# - 套接字异常 : "access denied" while opening port using UdpClient