通过Reflection.Emit生成可移植类库

Tri*_*ian 9 .net reflection.emit portable-class-library

我正在编写一个使用System.Reflection.EmitAPI 生成磁盘上.NET程序集的编译器.编译器本身是针对.NET 4.5构建的,但生成的代码仅引用可移植类库中的类型.但是,当尝试从Windows Phone 8项目引用生成的程序集时,Visual Studio会抱怨这一点A reference to a higher version or incompatible assembly cannot be added to the project.

在反编译器中打开生成的程序集时,我可以看到它引用了两个PCL加上mscorlib 4.0.0.0,而我理解PCL应该引用mscorlib 2.0.5.0.

有没有办法让System.Reflection.EmitAPI生成PCL,或者是我唯一的选择迁移到Mono.Cecil

Tri*_*ian 5

好的,我会回答我自己的问题.

我没有发现任何证据表明System.Reflection.EmitAPI可以生成引用另一个版本的程序集,而mscorlib不是当前进程使用的版本.实际上,带System.Type参数和其他反射对象的API 可能会添加对查询其Type.Assembly属性的结果的引用,这与使用中的版本相对应mscorlib.

但是,可移植类库与System.Reflection.Emit生成的库没有什么不同,因此可以在事件之后修补程序集以"使它们可移植".免责声明:这需要熟悉PE文件格式,可能会产生无法预料的副作用,但它对我有用:

  • 生成程序集时,用于AssemblyBuilder.SetCustomAttribute将此属性添加到程序集:

    [System.Runtime.Versioning.TargetFrameworkAttribute(".NETPortable,Version=v4.0,Profile=Profile136", FrameworkDisplayName = ".NET Portable Subset")]
    
    Run Code Online (Sandbox Code Playgroud)
  • 这是粗略的地方:在调用之后AssemblyBuilder.Save,打开读/写文件流到生成的程序集,遍历PE,COFF,COM,CLI和元数据表头以找到AssemblyRef表行mscorlib.修改mscorlibto 的引用版本2.0.5.0,添加0x100到其标志("retargetable")并将其公钥标记blob更新为0x7CEC85D7BEA7798E.

请注意,如果您使用其他框架程序集,您可能还需要修补它们的引用(我没有对此进行测试).不然,瞧!该程序集现在是可移植的,例如,可以在Windows Phone项目中使用.

(...或只是使用Mono.Cecil/ IKVM.Reflection...)

编辑:我使用的代码可以在github上找到.这是一个巨大的黑客,所以通常的免责声明适用,使用风险自负.