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

Kob*_*uck 7 c# cil xamarin .net-core .net-standard

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

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

C#sizeofMarshal.SizeOf不适合这个,因为它们有很多限制.

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

我为.NET Standard&Core复制了这个,引用了我认为正确的mscorlib等价物.注意IL编译得很好,这个问题是另一个问题.

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

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

.assembly extern mscorlib {}
Run Code Online (Sandbox Code Playgroud)

.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
}
Run Code Online (Sandbox Code Playgroud)

.NET Core :(与标准相同,尽管我已经测试过两者)

.assembly extern System.Runtime
{
  .ver 0:0:0:0
}
Run Code Online (Sandbox Code Playgroud)

资源:

.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
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

我阅读了无数文章,帖子和存储库,详细说明了使用不同版本和程序集的错误.典型的解决方案似乎是添加对目标框架等效的mscorlib(破坏可移植性)的显式引用.似乎缺乏有关为.NET Standard&Core使用IL编译程序集的信息.

据我所知,.NET Standard&Core使用转发类型定义的外观,因此可以通过目标框架的运行时解析它们,从而实现可移植性.

我尝试过以下方法:

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

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

这似乎很明显,但我引用了错误的版本System.Runtime.

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

.净:

#define CORELIB "mscorlib"

.assembly extern CORELIB {}
Run Code Online (Sandbox Code Playgroud)

.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
}
Run Code Online (Sandbox Code Playgroud)

.NET核心:

#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
}
Run Code Online (Sandbox Code Playgroud)

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

Jac*_*ski 4

DotNet CoreFX 存储库中有一个很好的示例说明如何正确执行此操作。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>
Run Code Online (Sandbox Code Playgroud)