Portable Class Libraries were introduced with Visual Studio 2010 SP1 to aid writing libraries that could be used on many different platforms – the full .NET 4/4.5 framework, Windows Phone, Silverlight, Xbox, and Windows Store apps. You simply select which platforms and versions you want to target, then the available subset of APIs are magically available. But how does it work? How does Visual Studio know what it can target, and how does the same assembly run on many different platforms? Today, I’ll be finding out.

Creating a Portable Class Library

When you create a PCL in Visual Studio, you select the platforms and versions you want to target. In this example, I’ve selected everything at the lowest available version. In the project references list, this turns into a generic ‘.NET Portable Subset’, with no real identifying information as to what it actually is:

Hmm, ok, well lets see what the actual built assembly does with it. Lets create a field of type Action<T1,T2> so the assembly actually has something in it:

public class Class1 {
Action<int, double> action = null;
}

After building the assembly, and opening it up in a decompiler, we can see that that mysterious ‘.NET Portable Subset’ reference has turned into standard assembly references to mscorlib.dll and System.Core.dll. However, they look a bit odd:

mscorlib, Version=2.0.5.0, Culture=neutral,
PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes
System.Core, Version=2.0.5.0, Culture=neutral,
PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes

2.0.5.0 is the version number used by Silverlight assemblies, and that’s the Silverlight public key, but that ‘Retargetable’ flag is new. And if you have a look at the assembly-level attributes, you’ll spot something familiar:

[assembly: TargetFramework(
".NETPortable,Version=v4.0,Profile=Profile1",
FrameworkDisplayName=".NET Portable Subset")]

Aha! There’s the ‘.NET Portable Subset’ from the Visual Studio reference list. But what about the target framework? ".NETPortable,Version=v4.0,Profile=Profile1"? What’s that all about? Well, have a look in ‘C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0\Profile\’. In there is a list of every possible .NET 4 PCL subset you can target (22 in total). Within each profile directory are the available assemblies containing the types that can be used, and an xml file for each framework supported by that profile containing platform version information.

These profile directories have been pre-calculated by Microsoft and installed alongside visual studio. When you create a PCL project, and select the platforms and versions you want supported, Visual Studio looks at all the available profiles and the framework versions they are valid for. From the version and platform information in each profile it works out the most applicable profile, and dlls in that profile are the ones it compiles the PCL assembly against and to provide intellisense support.

But these dlls are useless at runtime. If you open one of the dlls in a decompiler, you’ll see that all the method bodies are empty or simply return the default value for the return type. These dlls exist only to be referenced and compiled against.

Using a portable class library

So the dlls in the Reference Assemblies folder are, rather unsuprisingly, only to be referenced. Something else happens at runtime to make the portable library work on all the supported frameworks.

It turns out that it all comes down to a feature of .NET assemblies that was introduced in .NET 2, and I looked at two years ago – type forwards. In the portable class library I’ve built, the System.Action`2 type I’ve used has been resolved to the System.Core assembly. In different platforms, it may be in different places. But every platform will either contain the type in System.Core, or System.Core will have a type forward to where the type is actually located.

So, as you’ll see in the framework-specific reference assemblies, Silverlight 4, Windows Phone, and Xbox all have System.Action`2 located in their System.Core.dll, so the type is resolved successfully on those platforms. Both the desktop and Silverlight 5 System.Core.dll have a type forward for System.Action`2 to the relevant mscorlib.dll, where the type is actually located.

Windows store applications (the framework for windows store applications is called ‘.NETCore’) forward the type to System.Runtime.dll. And, if you take a further look at the System.Core.dll in the .NETCore framework, this assembly contains no types whatsoever! The only things in that assembly of any note are a series of type forwards to various other assemblies in the .NETCore framework – that assembly exists only to redirect type references in portable class libraries when they are used in Windows Store applications.

Cross-version assembly references

There is one more thing we need to sort out. If you have a look at the assembly references in the original PCL we built, they reference a specific version of mscorlib.dll and System.Core.dll:

mscorlib, Version=2.0.5.0, Culture=neutral,
PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes
System.Core, Version=2.0.5.0, Culture=neutral,
PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes

These versions are the same as the version numbers on Silverlight 4, Windows Phone, and Xbox framework assemblies. But the version of mscorlib for Silverlight 5 is:

mscorlib, Version=5.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e

and .NET 4 desktop and .NETCore:

mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

This is a problem. These assemblies all have strong name signatures, and the version & public key form part of the assembly’s identity. An assembly reference to an assembly with version 2.0.5.0 and public key 7cec85d7bea7798e cannot be resolved to an assembly with version 4.0.0.0 and public key b77a5c561934e089. To the CLR, these are two completely different assemblies.

That’s where the Retargetable flag on the assembly references comes in. If this flag is on an assembly reference, it means the reference can resolve to an assembly with a different version and public key, even though it is technically a different assembly. This flag is on all the references to framework dlls in a PCL, and this means the PCL can run on different frameworks with different assembly versions and public keys. The framework dll references are resolved to the one available in the framework the library is executing on at runtime.

Conclusion

There’s nothing magic about portable class libraries. They are compiled just like any other assembly, but are compiled against a specific pre-defined subset of the libraries available in the different frameworks, defined by portable profiles representing the various combinations of types and methods available. When the library is executing on a specific framework at runtime, type fowards redirect any types that have been moved to a different assembly in that framework. The common CLR, assembly metadata and IL formats across all the frameworks and versions ensure the actual code logic in the assembly executes the same way on any available framework.

from:https://www.simple-talk.com/blogs/2013/04/19/inside-portable-class-libraries/

最新文章

  1. centos7 安装时候检测不到空余硬盘的解决办法
  2. 【bzoj1596】[Usaco2008 Jan]电话网络
  3. a bitwise operation 广告投放监控
  4. 灵活控制 Hibernate 的日志或 SQL 输出(包含参数),以便于诊断
  5. IllegalStateException
  6. 汇总前端最最常用的JS代码片段
  7. PHP实现单击“添加”按钮增加一行表单项,并将所有内容插入到数据库中
  8. DB2 中日期 比较
  9. 如何把mysql的ID归0?
  10. C#之系统异常处理机制
  11. Knockoutjs:Component and Custom Elements(翻译文章)
  12. 项目Contact开发中遇到的,引以为戒
  13. 从web图片裁剪出发:了解H5中的canvas
  14. python取txt文件的若干行到另一个文件
  15. Python - 去除list中的空字符
  16. kafka.common.KafkaException: Failed to acquire lock on file .lock in /tmp/kafka-logs. A Kafka instance in another process or thread is using this directory.
  17. python 格式化字符串&quot;%s&quot;%
  18. jQuery对象的获取与操作方法总结
  19. P3932 浮游大陆的68号岛
  20. rapidjson库的基本使用

热门文章

  1. 经典面试题:n个数字(0,1,…,n-1)形成一个圆圈
  2. Centos7配置vsftpd3.0.2
  3. git/github 生成密钥
  4. CF 586B 起点到终点的最短路和次短路之和
  5. GUC-11 线程池
  6. 铁轨(UVa 514)
  7. java.lang.NoClassDefFoundError: javax/persistence/EntityListeners
  8. JMeter导入jmx运行脚本时出现这样的错误jmeter.save.SaveService: Conversion error com.thoughtworks.xstream.converters.ConversionException:
  9. mysql find_in_set函数详解
  10. Mysql远程连接报错:SQL Error (1130): Host &#39;192.168.61.128&#39; is not allowed to connect to this MySQL server