自定义.NET可移植类库档案文件配置文件?

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下:

  • 所有程序集都是存根,并设置了“Retargetable”属性。
  • 所有程序集的标志均为0x171:0x001已签名,0x100可重定向,0x070在AssemblyNameFlags中未定义(似乎没有影响)
  • 所有程序集之间的引用也具有“Retargetable”属性。
  • 支持Silverlight的所有程序集的版本均为2.0.5.0。
  • 构建的PCL二进制文件包含每个被引用程序集的两个引用(例如mscorlib 2.0.5.0可重定向+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代码(如果可能的话)。


2
我知道你并不是在寻找一个答案来告诉你停止,但是认为你可以随意选择框架中想要包含的部分,然后它就能正常工作,这个想法真的不明智。即使你找到了某种让它在某种程度上工作的笨拙方法,迟早你会遇到问题 - 到那时你将浪费很多时间。 - Jon Skeet
你假定你已经足够了解支持这些API所需的其他所有依赖项,无论是本地代码还是其他托管代码。我认为情况并不像你预期的那么简单。 - Jon Skeet
2
仅仅因为在两个平台上具有相同的可观测行为,并不意味着在两个平台上的实现方式相同。 即使对于某些方法,也并不意味着对于所有方法都是如此。 核心库并没有被设计成可以像这样混合和匹配使用。 - Jon Skeet
我明白了,但在我意识到区别之前对我来说也没有什么影响。在迁移到PCL之前,我有两个版本的完全相同的代码拷贝(或链接)。 - AqD
抱歉,我完全不理解你最后的评论。但我仍然建议你放弃这个尝试;我认为它不会有好结果。 - Jon Skeet
显示剩余3条评论
2个回答

10
由于您不接受“不”或“停止”作为答案,让我试着解释一下为什么这是一个坏主意。简短的答案是:如果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's mscorlib的引用视为对桌面版本的引用。这是有效的,因为Silverlight's mscorlib被设计为.NET Framework版本的子集(特定版本的组合)。
虽然这听起来像是解决所有平台差异的万能药,但实际上并不是。随着时间的推移,不同的平台选择了不同的程序集分配方式。例如,ICommand在.NET 4和Silverlight 4中都可用。但是,在WPF中,它是在PresentationCore.dll中实现的,而Silverlight则将其放在System.Windows.dll中。为了理解为什么在针对.NET 4和Silverlight 4时PCL不公开ICommand,请看看如果PCL公开它会发生什么。
在PCL中,我们必须将ICommand放在某些程序集中。我们可以选择使用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,因为以前的版本没有类型转发。
我们正在与平台所有者合作,以填补所有应该公开但未公开的API。我们基本上有两种策略:
  1. 我们要求平台所有者添加缺失的API或类型转发。
  2. 我们以一种独立于主流发布方式的便携式实现形式发布,例如AsyncHttpClient
然而,“仅仅”将其添加到PCL并不起作用。 编辑:如果您在PCL中缺少某个功能,则始终可以解除阻止。我们的测试人员Daniel已经编写了一篇博客文章,其中展示了一些技术可供使用。

感谢。但在微软将我需要的内容添加到 PCL 之前,对我来说没有替代方案。我不能将非解决方案作为解决方案。 - AqD
你可以解决这个问题,而无需调整PCL参考程序集。我们的测试人员编写了一篇关于可能解决方案的优秀博客文章。我已经将其添加到答案中。 - Immo Landwerth

4

不行,停止。

好的,既然这不是你想听到的,那么继续风险自负。:) 这当然不受支持,我也不是律师,所以我不知道许可协议是否允许。

听起来你现有解决方案的主要问题是你不能选择单独的API添加到可移植配置文件中。为了做到这一点,你可以使用现有的参考程序集上的ildasm,然后添加你想要的API(可能是通过从运行在其他参考程序集上的ildasm的结果中复制它们),然后使用ilasm创建自己的版本的参考程序集,并将这些额外的API添加进去。

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

另一个选择是使用类型转发,如我的答案here 中所述。在这种情况下,你最终的代码可以直接共享,但会依赖于每个平台都不同的DLL。


类型转发似乎是一个不错的想法。理论上,应该可以包装所有API的交叉部分,然后将它们转发到正确的程序集中。但是必须有一个工具来自动化这个过程... - AqD

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