7

我需要添加和/或修改配置文件以允许在 PCL 中共享更多类和成员(其中许多是内置在框架中的,例如 Thread.Sleep)。最好的方法是什么?有什么工具可以帮助做到这一点吗?



PS:我不是在寻找一个回答者来告诉我 NO 或 STOP。我想拥有可以在不同环境中共享的一次编译 DLL。没有每个平台的二进制文件,没有重新编译,没有 ifdef。


以下是我到目前为止得到的:

要求:

  • 目标环境:Silverlight 5 和 .NET Framework 4.5。
  • PCL 的用途:RIA 客户端和 ASP.NET 服务器共享基础架构(没有 WCF)
  • 默认配置文件中缺少什么:XPath、线程方法、DynamicMethod/ILGenerator

PCL 配置文件:Reference Assemblies\Microsoft\Framework.NETPortable下:

  • 所有程序集都是存根,具有“可重定向”属性集。
  • 所有程序集都有标志 = 0x171:0x001 已签名,0x100 可重定向,0x070 在 AssemblyNameFlags 中未定义(似乎没有效果)
  • 程序集之间的所有引用也具有“可重定向”属性。
  • 支持 Silverlight 的所有程序集的版本均为 2.0.5.0。
  • 构建的 PCL 二进制文件包含每个引用程序集的两个引用(例如:mscorlib 2.0.5.0 retargetable + mscorlib 4.0)

定制尝试#1

  • 配置文件:Silverlight 5 + .NET Framework 4.5(配置文件 24)
  • 将 SL5 mscorlib.dll 复制到配置文件 24
  • 将 SL5 mscorlib.dll 标记为可重定向(更改为延迟签名)
  • ReSharper:无法解析所有扩展方法,泛型类型/值匹配错误
  • 构建:成功,运行:成功

自定义尝试#2

  • 配置文件:Silverlight 5 + .NET Framework 4.5(配置文件 24)
  • 将所有 SL5 DLL 复制到配置文件 24
  • 将所有 SL5 DLL 标记为可重定向(更改为延迟签名)
  • 将 SL5 DLL 之间的所有引用标记为可重定向
  • ReSharper:无法解析所有扩展方法,泛型类型/值匹配错误
  • 构建:成功,运行:成功

定制尝试#3

  • 配置文件:Silverlight 4 + .NET Framework 4.0.3(配置文件 18)
  • 将 SL4 mscorlib.dll 复制到配置文件 18
  • 将 SL4 mscorlib.dll 标记为可重定向(更改为延迟签名)
  • ReSharper:成功
  • 构建:成功,运行:成功

定制尝试#4

  • 配置文件:Silverlight 4 + .NET Framework 4.0.3(配置文件 18)
  • 将所有 SL4 DLL 复制到配置文件 18
  • 将所有 SL4 DLL 的 .NET 运行时版本设置为 v4(原始 DLL 有,效果未知)
  • 将所有 SL4 DLL 标记为可重定向(更改为延迟签名)
  • 将 SL4 DLL 之间的所有引用标记为可重定向
  • ReSharper:成功
  • 构建:成功,运行:成功

自定义尝试 #5继承 #4

  • 配置文件:Silverlight 4 + .NET Framework 4.0.3(配置文件 18)
  • 将 SL4 的 System.Numerics(包含在其他 SL 配置文件中)添加到 RedistList\FrameworkList.xml
  • 将 SL4 的 System.Xml.XPath(不包含在任何 SL 配置文件中)添加到 RedistList\FrameworkList.xml
  • 结果:无法从默认 PCL 引用中解析 System.Numerics 和 System.Xml.XPath
  • 修复:手动引用两个 DLL - 无法强制它们可重定向,但由于下面提到的问题,VS 不会使用不可重定向的 System.Numerics 或 System.Xml.XPath 进行编译

笔记:

  • 编译错误:“...在未引用的程序集中定义,您必须添加对程序集的引用”。在所有程序集都可重定向但它们之间的引用之一未更改为“可重定向”之后发生

它在一定程度上起作用,但定制现有的引用 DLL 或添加新的 DLL 非常麻烦,而且在覆盖引用的 DLL 之后(如果可能的话)也不能轻松地验证 PCL 代码。

4

2 回答 2

10

由于您不会将NOSTOP作为答案,让我尝试解释为什么这是一个坏主意。简短的回答是:如果 PCL 不公开 API,通常是因为它不起作用。

首先,PCL 没有自己的 API 集。PCL 只是公开了您想要定位的一组给定平台之间的 API 交集。现在,在某些情况下,API 级别的交集会产生比 PCL 公开的更多的 API。为什么会出现这种情况有几个原因。

  1. API 并非在所有平台上都可用
  2. API 可用,但实际上不起作用
  3. API 在不同的程序集中定义

第一个应该很明显。PCL 本身并不是一个实际的平台,而只是公开了那里的内容。因此,我们无法为您提供实际上并不存在于您所针对的所有平台上的 API。毕竟,我们暴露了交叉点。

第二个听起来有点奇怪,但确实发生了。以 Windows Phone 7 上的文件 IO 为例。虽然File 类在技术上在 Windows Phone 上可用,但它被记录为

此类型的存在是为了支持 Silverlight for Windows Phone 中的 .NET Compact Framework 基础结构,并且不打算在您的应用程序代码中使用。

你可能会说“我在乎什么?” 并简单地尝试一下,但随后您会发现手机上的安全模型会阻止您访问您正在查找的文件。所以在 PCL 中公开这个 API 并没有帮助。事实上,我们的团队认为这实际上会伤害您,因为它会引导您走上一条不可移植的道路。

围绕在不同程序集中实现的 API 的第三个问题涉及更多。为了理解为什么这是一个问题,您需要考虑 CLR 通常如何处理 API。CLR 没有加载单个类型的概念,例如 Java。在 .NET 中,类型是在程序集中实现的,为了使用它们(“加载它们”),您需要加载定义该类型的程序集。在幕后,类型引用包括命名空间限定类型名称以及定义该类型的程序集。通常,具有相同命名空间限定名称但存在于不同程序集中的类型被认为是不同的。例如,类型MyNamespace.MyType, Assembly1MyNamespace.MyType, Assembly2. 请注意,程序集本身也有一个完全限定名称的概念;它包括程序集名称和公钥标记。这允许两家公司同时生产一个名为“Foo”的程序集,而不会混淆 CLR(当然,假设它们使用不同的密钥进行签名)。所以本质上加载类型需要几个步骤:找到定义类型的程序集,加载该程序集,然后加载类型。

通常,不同的 .NET 平台对平台程序集使用不同的密钥,例如 mscorlib。现在您可能想知道如何在 .NET Framework 上使用 Silverlight 中的类型。原因是CLR有汇编统一的概念。程序集统一允许 .NET Framework 将对 Silverlight 的 mscorlib 的引用视为对桌面版本的引用。这是有效的,因为 Silverlight 的 mscorlib 被设计为 .NET Framework 版本的子集(对于特定的版本组合)。

虽然这听起来像是弥合所有平台差异的灵丹妙药,但实际上并非如此。随着时间的推移,不同的平台选择了不同的装配因子。以 ICommand 为例。它在 .NET 4 和 Silverlight 4 中可用。但是,在 WPF 中,它在 PresentationCore.dll 中实现,而 Silverlight 将它放在 System.Windows.dll 中。要了解为什么 PCL 在面向 .NET 4 和 Silverlight 4 时不公开 ICommand,让我们看看如果 PCL 公开它会发生什么。

在 PCL 中,我们必须将 ICommnad 放入某个程序集中。我们可以选择使用 Silverlight 程序集或完整的框架之一。无论我们选择哪一个,该类型都不会在另一个平台上解析,因为 PresenationCore.dll 仅存在于 .NET 4 中,而 System.Windows.dll 仅存在于 Silverlight 4 中。

我们通过允许 System.Windows.dll 中的 ICommand 引用在完整框架上成功解决了这个问题。我们是怎么做到的?答案是类型转发。类型转发允许程序集说“我定义了一个类型 Foo”。当 CLR 尝试从该程序集加载类型 Foo 时,程序集实际上说“不,不 - Foo 类型实际上是在另一个程序集 Bar 中定义的”。换句话说,程序集 Foo 包含类似指向 Bar 的类型版本的指针。我们将这些指针称为转发条目。

这个概念允许我们通过将 System.Windows.dll 添加到包含转发到实际实现的类型的完整框架中来解决 ICommand 不匹配问题。PCL 现在在 System.Windows.dll 中为您提供 ICommand,并且可以确保类型加载请求可以在 .NET Framework 和 Silverlight 上成功。但是,这需要至少以 .NET Framework 4.5 为目标,因为以前的版本没有 forward 类型。

对于所有应该公开但未公开的 API,我们正在与平台所有者合作以缩小差距。为此,我们基本上有两种策略:

  1. 我们要求平台所有者添加缺少的 API 或类型转发
  2. 我们以带外方式发布了一个可移植的实现。以AsyncHttpClient为例。

但是,“只是”将其添加到 PCL 是行不通的。

编辑:如果您错过了 PCL 中的某个功能,您可以随时解锁。我们的测试员 Daniel写了一篇博文,展示了一些您可以使用的技术。

于 2013-05-05T19:47:50.607 回答
4

不。停止。

好的,既然这不是您想听到的,请继续自担风险。:) 这当然不被支持,IANAL 所以我也不知道许可协议是否允许这样做。

听起来您现有解决方案的主要问题是您无法选择单个 API 添加到可移植配置文件中。为此,您可以在现有参考程序集上使用 ildasm,然后添加所需的 API(可能通过从在另一个参考程序集上运行 ildasm 的结果复制它们),然后使用 ilasm 创建您自己的参考版本具有这些附加 API 的程序集。

您需要延迟签名和/或禁用以这种方式修改的程序集的密钥的强名称密钥验证。

另一种选择是使用类型转发,如我在此处的回答中所述。在这种情况下,您最终会得到您的主要代码可以按原样共享,并且依赖于每个平台都不同的 DLL。

于 2013-05-05T14:20:22.783 回答