如何在便携式类库中使用 TypeForwardedTo?

8
我正在尝试构建一个便携式类库,该类库在可用时使用平台的实现。例如,Lazy<T>在.NET 4.5、Windows Store应用程序、Windows Phone 8上都可用,但在Windows Phone 7、Silverlight 4上不可用。当我的PCL加载到具有Lazy<T>实现的平台上时,我想使用平台的实现。当平台上没有可用时,我想使用自己的实现。似乎这是可能的,因为Microsoft BCL正在这样做,但我还没有弄清楚如何实现它。
我读到通过使用TypeForwardedToAttribute,可以重定向PCL以使用平台的实现。我不太确定如何配置我的Visual Studio项目以实现此结果。如果CoreLib是我的库,并且ShimLib包含了我对Lazy<T>的实现。在哪里添加TypeForwardedToAttribute?该属性需要一个实际的Type引用typeof(System.Lazy<>),但在PCL中定位Windows Phone 7时不起作用。如果我删除Windows Phone 7,那么我就不能从CoreLib添加对ShimLib的引用,因为ShimLib不支持CoreLib所支持的所有平台。我该如何处理?
是的,我知道Lazy<T>很容易实现,但这只是一个示例,我的实际情况适用于许多更不容易实现的类。

[TypeForwardedTo] 实际上并没有解决底层的部署问题。在 PCL 的情况下,微软会负责部署可能包含 [TypeForwardedTo] 的程序集,具体取决于平台。但是对于您自己转发的类,您将无法获得同样的帮助。 - Hans Passant
如果您忽略 LazyThreadSafetyMode,那么 Lazy<T> 就“超级容易实现”。别担心,我为您提供了解决方案:Theraot's Lazy<T> for .NET 3.5 or previous。如果需要替代方案,请查看 LazyNeedle<T>(请报告任何错误)。完全透明度披露:是的,我就是 Threaot。 - Theraot
2个回答

8
Microsoft.Bcl采用的方法是发布两个具有相同标识的程序集,一个包含本身类型,另一个包含类型转发。在针对不支持Lazy的平台(包括其中之一的可移植库组合)时,引用包含类型的那个程序集。而在针对支持Lazy的平台时,则引用包含类型转发的那个程序集,这样可以消费已经建立在旧平台上的库。
需要注意的是,Microsoft.Bcl具有一点优势,而你没有。我们提供了一个与后续版本中已存在的程序集具有相同标识的程序集,这意味着当运行Windows Phone 7应用程序在Windows Phone 8上时,它们将获得内置版本,而不是shim库中的版本。虽然你无法模仿这一点,但在你的情况下,这可能是可以接受的。

1
这正是我试图模仿的内容,而无法复制BCL的身份似乎是在困扰着我。现在对我来说有意义了。懒惰只是我认为很容易说明的一个例子。我的实际实现正在尝试为WP7挤入Windows.Storage API,以便我可以为WP8、W8和WP7编写一个存储API。在W8上,我希望我的公共API公开真正的Windows.Storage类型。在WP7上,它们将是我实现的StorageFile、StorageFolder等。 - Matt Dotson
3
哦,我理解你的问题。Windows.Storage几乎不可能模仿。它是一个WinRT API。WinRT受编译器的约束方式与传统的.NET API不同,这将在Windows Phone 7上模仿变得不可能,因为运行时对其一无所知。相反,我建议使用像Daniel Plaisted的PCL Storage这样的东西,它提供了IO API的包装器:http://pclstorage.codeplex.com/ - David Kean

5
类型转发的原理在这个问题和这篇文章中已经很好地解释了,我不再赘述细节。然而,简而言之,这个想法是使库A能够重用,即使它引用了正在被库C替换的库B,也不需要重新编译。为此,库B必须被修改,以便将引用转发到库C,这正是TypeForwardedTo属性所做的。

这对你有什么帮助?那么,你可以创建一个ShimLib,让它被你所有的项目引用,但使用条件编译和类型转发来将它链接到框架库(如果存在)。幸运的是,有人已经为你做了 这个 :)

编辑回应Matt的评论:你是对的,我忽视了Theraot不会回退到原始实现。而且我认为这将很难在不重新编译的情况下实现。你能做的最好的事情可能是遵循这个策略,即为不同的框架版本拥有不同的构建配置。这样,你就可以条件地编译TypeForwardedToAttribute。至少,这样可以避免重复代码。如果你真的不想分发不同版本的代码,可以实现一个启动程序,确定框架版本并加载已经使用针对此版本的构建配置编译的你的程序集的版本。


我理解TypeForwardedTo属性的概念,而且我已经在普通程序集中让它工作了。但是我遇到的问题是将其与可移植类库结合使用。Theraot很有趣,但它不是PCL,并且如果存在框架的实现,它不会推迟到框架的实现。它正在为特定版本的框架创建库。我所追求的是像Microsoft.Bcl(http://www.nuget.org/packages/Microsoft.Bcl/)一样,其中Phone7使用BCL的可移植Task实现,但Phone8和WinRuntime使用框架的Task实现。 - Matt Dotson
谢谢大哥。不同的构建配置很接近,但我无法根据配置更改PCL项目的目标框架属性。该属性在项目中设置一次,而与配置无关。如果我无法更改目标框架以排除WP7,则会出现构建错误,因为编译器无法解析TypeForwardedTo属性中的System.Lazy。也许创建生成相同程序集名称的多个项目是唯一的策略。 - Matt Dotson
也许这篇帖子可以帮助你:https://dev59.com/oXA85IYBdhLWcg3wJf8Z#2928835 - bigge
@MattDotson 和 bigge,确实存在前面提到的后备不足的问题...事实上,你们给了我一个想法...我可以创建一个外观程序集,它针对.NET 2.0(因此您可以将其包含在大多数项目中),让它检测并调用正确的程序集。这会增加开销,但可以选择使用它。你对此有什么看法?(虽然需要进行大量测试)。关于PCL...我会处理的。注:除了System.Threading.Tasks之外,我目前正在寻求针对.NET Compact Framework的目标(没有日期)。 - Theraot

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