使用从.NET编译的程序集与.NET Core& Xamarin

时间:2017-10-31 00:49:40

标签: c# xamarin .net-core cil .net-standard

更新了适用于我的解决方案。请参阅此问题的底部。

上下文
我需要一种方法来评估泛型类型的大小,以便计算适合某个字节大小的数组长度。基本上,  sizeof类似于C / C ++提供的内容。

C#' sizeofMarshal.SizeOf不适用于此,因为它们有很多限制。

考虑到这一点,我在IL中编写了一个程序集,通过sizeof操作码启用了我正在寻找的功能。我知道它基本上使用引用类型评估为IntPtr.Size

我为.NET Standard& amp;核心,引用我认为是mscorlib的正确等价物。注意IL编译得很好,这个问题是另一个问题。

代码:
每个目标框架的标题:

.NET:(Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ ilasm.exe)

.assembly extern mscorlib {}

.NET标准:(从nuget提取的ilasm)

.assembly extern netstandard 
{ 
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89)
  .ver 0:0:0:0
}
.assembly extern System.Runtime
{
  .ver 0:0:0:0
}

.NET核心:(与标准相同,虽然我已经对两者进行了测试)

.assembly extern System.Runtime
{
  .ver 0:0:0:0
}

来源:

.assembly Company.IL
{
  .ver 0:0:1:0
}
.module Company.IL.dll

// CORE is a define for mscorlib, netstandard, and System.Runtime
.class public abstract sealed auto ansi beforefieldinit 
  Company.IL.Embedded extends [CORE]System.Object
{
  .method public hidebysig specialname rtspecialname instance void 
    .ctor() cil managed 
  {
    .maxstack 8
    ldarg.0
    call instance void [CORE]System.Object::.ctor()
    ret
  }

  .method public hidebysig static uint32 
    SizeOf<T>() cil managed 
  {
    sizeof !!0
    ret
  }
}

问题:
当以这种方式编译的任何DLL被.NET Core或Xamarin应用程序引用时,我收到以下错误:

  

类型&#39;对象&#39;在未引用的程序集中定义。   您必须添加对程序集&System; Run.Runtime,Version = 0.0.0.0的引用,   Culture = neutral,PublicKeyToken = null&#39;。

当.NET项目或.NET标准库引用这些dll然后由.NET项目引用时,不会发生此问题。

我已经阅读了无数文章,帖子和存储库,详细说明了使用不同版本和程序集的错误。典型的解决方案似乎是添加对目标框架的明确引用,相当于mscorlib(破解可移植性)。似乎缺乏有关将.NET编译程序集用于.NET Standard&amp;芯

根据我的理解,.NET Standard&amp;转换类型定义的核心使用外观,以便它们可以由目标框架的运行时解析,从而实现可移植性。

我尝试过以下方法:

  • System.Runtime
  • 的明确版本
  • 使用完全相同的引用来反汇编从C#编译的.NET Core库。奇怪的是,它们似乎针对显式版本(例如.NET Core 2.0中的System.Runtime 4.2)。
  • 使用Emit API动态生成代码。编译到内存似乎有效,但不是一个选项,因为我也瞄准Xamarin.iOS(仅限AOT)。从磁盘引用此类动态编译的程序集会产生与我手动编译它们相同的错误。

更新
我尝试了Jacek's answer中的解决方案(遵循构建说明here),但是无法配置我的系统以使用构建脚本或VS 2017编译corefx。但是,在深入研究代码之后System.Runtime.CompilerServices.Unsafe我发现了一个解决方案。

这可能看起来很明显,但我引用了System.Runtime的错误版本。

每个目标框架的标头(从corefx复制):

.NET:

#define CORELIB "mscorlib"

.assembly extern CORELIB {}

.NET标准:

#define CORELIB "System.Runtime"
#define netcoreapp

// Metadata version: v4.0.30319
.assembly extern CORELIB
{
  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )
  .ver 4:0:0:0
}

.NET Core:

#define CORELIB "System.Runtime"

// Metadata version: v4.0.30319
.assembly extern CORELIB
{
  .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A )
  .ver 4:0:0:0
}

在所有源文件中,使用CORELIB引用mscorlib中的类型(即[CORELIB]System.Object)。

2 个答案:

答案 0 :(得分:3)

有一个很好的例子说明如何在DotNet CoreFX repo中正确地完成它。 System.Runtime.CompilerServices.Unsafe是一个仅限IL的程序集,可供.NET Core和Xamarin使用。

https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe

有两种方法可以解决这个问题:(i)尝试从头开始重新创建项目中构建配置的必需元素 - 这将耗费大量时间并且非常复杂 - corefx构建系统非常复杂,(ii)使用现有的通过复制System.Runtime.CompilerServices.Unsafe项目,更改命名以及用您的代码替换IL代码,构建基础架构并在.NET Core CoreFX仓库中创建IL项目。在我看来,这是构建基于IL的装配的最快方法,可以保证与所有目标一起正常工作。

要针对任何特定版本的.NET Core或.NET Standard构建程序集,只需在发布分支中创建它:release/2.0.0release/1.1.0等。

  

“Object”类型在未引用的程序集中定义。您必须添加对程序集'System.Runtime,Version = 0.0.0.0,Culture = neutral,PublicKeyToken = null'的引用。

有一个选项可以尝试在引发触发它的程序集中单独抑制编译错误。将以下属性设置为新格式​​csproj / vbproj应该禁止它:

<PropertyGroup>
    <_HasReferenceToSystemRuntime>true</_HasReferenceToSystemRuntime>
</PropertyGroup>

答案 1 :(得分:0)

好的,这是我研究的结果(基于https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafehttps://github.com/jvbsl/ILProj):

global.json

{
  "msbuild-sdks": {
    "Microsoft.NET.Sdk.IL": "3.0.0-preview-27318-01"
  }
}

ILProj\ILProj.ilproj

<Project Sdk="Microsoft.NET.Sdk.IL">

    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
        <MicrosoftNetCoreIlasmPackageVersion>3.0.0-preview-27318-01</MicrosoftNetCoreIlasmPackageVersion>
        <IncludePath Condition="'$(TargetFramework)' == 'netstandard1.0'">include\netstandard</IncludePath>
        <IncludePath Condition="'$(TargetFramework)' == 'netstandard2.0'">include\netstandard</IncludePath>
        <IncludePath Condition="'$(TargetFramework)' == 'netcoreapp1.0'">include\netcoreapp</IncludePath>
        <IncludePath Condition="'$(TargetFramework)' == 'netcoreapp2.0'">include\netcoreapp</IncludePath>
        <IlasmFlags>$(IlasmFlags) -INCLUDE=$(IncludePath)</IlasmFlags>
    </PropertyGroup>

</Project>

ILProj\include\netstandard\coreassembly.h

#define CORE_ASSEMBLY "System.Runtime"

// Metadata version: v4.0.30319
.assembly extern CORE_ASSEMBLY
{
  .publickeytoken = ( B0 3F 5F 7F 11 D5 0A 3A )
  .ver 4:0:0:0
}

ILProj\Class.il

#include "coreassembly.h"

.assembly ILProj
{
  .ver 1:0:0:0
}

.module ILProj.dll

.class public abstract auto ansi sealed beforefieldinit ILProj.Class
  extends [CORE_ASSEMBLY]System.Object
{
  .method public hidebysig static int32 Square(int32 a) cil managed
  {
    .maxstack 2
    ldarg.0
    dup
    mul
    ret
  }
}

如果您很难将所有内容放在一起,请在此处查看示例:https://github.com/Konard/ILProj