为什么我不能在我的可移植类库中调用Delegate.CreateDelegate方法?

10
我有一个问题:我想从我的可移植类库中调用 Delegate.CreateDelegate 方法,该类库针对 .NET 4.5、Windows Phone 8 和 Windows 8 Store 应用程序,但我的代码无法编译。编译器说它找不到 Delegate 类上的方法。
有趣的是,例如微软的 PRISM 库可以从可移植类库中调用“Delegate.CreateDelegate”方法。 它在 DelegateReference 类中执行此操作。 PRISM 可移植类库的目标是 .NET 4.0、Windows 8 Store 应用程序、Windows Phone 8 和 Silverlight 5(因此更为严格)。
无法编译的代码如下:
public class MyClass
{
    public void MyMethod<T>(EventHandler handler)
    {
        var @delegate = Delegate.CreateDelegate(typeof (OpenEventHandler<T>), null, handler.GetMethodInfo());
    }
}

public delegate void OpenEventHandler<in T>(T target, object sender, EventArgs arguments);

这里可以下载一个示例:https://dl.dropboxusercontent.com/u/14810011/PortableClassLibraryReferenceProblem.zip,其中包含我的库项目和PRISM PubSubEvents项目的一个非常简化的版本,只包含DelegateReference类及其接口。后者的完整源代码可以在此处找到:http://prismwindowsruntime.codeplex.com/SourceControl/latest。我该怎么做才能使用所有委托成员?提前感谢您的帮助!
Henk Holterman的回答后进行了编辑:
GetMethodInfo()是由PCL子集支持的扩展方法。无论如何,这与我无法调用Delegate.CreateDelegate没有关系,而PRISM的PCL项目可以。
Hans Passants评论后进行了第二次编辑:
我刚刚玩了一下,发现当我将Silverlight 5作为可移植库的目标激活时,确实可以访问Delegate.CreateDelegate(而GetMethodInfo扩展方法不再可用)。那么,Delegate.CreateDelegate是否在内部映射到另一个Windows 8 Store和Phone应用程序的API呢?这是我能想到的唯一方式,即通过添加Silverlight 5作为有效目标来突然访问此方法。
(您可以通过右键单击“MyPortableClassLibrary”项目,单击“属性”,然后在“库”选项卡中单击更改以选择可移植库所针对的框架来重现此操作。)
另外,今天早些时候,我创建了一个Windows Store应用程序项目,并发现在.NET for Windows Runtime的Delegate类上没有定义CreateDelegate方法。
在我的实际项目中,我不想将Silverlight 5作为目标,因为我使用Rx大量使用IObservable和IObserver这些接口在Silverlight中未定义。

1
在我的机器上完全正常运行。 - Hans Passant
@Hans 我注意到你没有下载示例代码?因为它在那里无法工作。无论如何,我已经更新了我的问题,包括最近的发现。 - feO2x
1
这是可移植库的一个问题,某些子集提供不同的API,我曾经遇到过一些反射类型在Windows 8/Phone子集上不可用,直到我将目标设置为Silverlight。 - Rafael
2个回答

13

好的,经过一晚上的睡眠后,我意识到我的问题实际上应该是 "如何在引入了Windows运行时的新API中动态创建委托?" 如Rafael在我的问题评论中指出的那样,与.NET相比,当针对Windows 8 / Phone 8时提供了不同的API。如果还针对Silverlight,则将映射未在Windows 8 / Phone 8中可用的API,这就解释了为什么我可以在将Silverlight添加为便携式类库的目标时突然调用Delegate.CreateDelegate。在.NET中,针对反射的新API是在.NET 4.5中引入的。

总之,在Windows 8 / Windows Phone 8中创建委托,必须使用MethodInfo.CreateDelegate方法,就像这样:

public class MyClass
{
    public void MyMethod<T>(EventHandler handler)
    {
        var methodInfo = handler.GetMethodInfo();
        var @delegate = methodInfo.CreateDelegate(typeof(OpenEventHandler<T>), null);
    }
}

public delegate void OpenEventHandler<in T>(T target, object sender, EventArgs arguments);

10
您添加/删除Silverlight时见到的是两个不同的.NET API表面之间的便携式切换。我在这里介绍这两个不同的表面:什么是 .NET Portable Subset(Legacy)?
在我们称之为传统表面区域中,此方法存储在Delegate上。在新的表面区域中,此方法已移至MethodInfo。 为什么这样做呢? 出于分层原因。在新的表面中,反射类型(即Assembly、MemberInfo、MethodInfo等)被视为比核心基元(包括Delegate)更高层次的存在。与遗留表面区域不同(其中它们都在mscorlib中),这些类型位于不同的程序集中:System.Reflection.dll和System.Runtime.dll。
一些方法(其中包括此方法)导致较低层级(System.Runtime.dll)的某些东西依赖于较高层级(System.Reflection.dll)的某些东西。为了防止这种情况,已将依赖关系颠倒过来。

谢谢你的额外见解,David。 - feO2x

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接