c# - 使用通过 .NET Core 和 Xamarin 从 IL 编译的程序集

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

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

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

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

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

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

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

.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 Core:(与标准的 ilasm 相同,尽管我已经对两者进行了测试)

.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
  }
}

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

The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly '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's answer 中的解决方案(遵循构建说明 here ),但无法将我的系统配置为使用构建脚本或 VS 2017 编译 corefx。但是,在深入研究 System.Runtime.CompilerServices.Unsafe 的代码之后我发现了一个解决方案。

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

每个目标框架的 header (从 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 核心:

#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)。

最佳答案

在 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

The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.

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

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

关于c# - 使用通过 .NET Core 和 Xamarin 从 IL 编译的程序集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47026323/

相关文章:

c# - NET CORE MVC - 如何使用嵌套的多参数化路由

c# - 如何在 MonoMac C# 的派生对象上实现 Cocoa copyWithZone?

c# - 优化条件,否则xamarin iOS

c# - Xamarin Forms 等待动画或事件完成

c# - 禁用自动推送远程通知 ios

c# - 如何在 .NET Core 运行时设置 Sentry 日志级别?

c# - 如何在不重叠全屏窗口的情况下显示最顶层窗口

c# - 使用覆盖调用基类方法 *WITHOUT*

c# - 如何在 ASP.NET Datagrid 中弹出日历?

c# - 应用程序在 Linux 上运行时连接到 MSSQL Server 2017 超时