使用从 IL 编译的程序集和 .NET Core &沙马林 [英] Using assemblies compiled from IL with .NET Core & Xamarin

查看:30
本文介绍了使用从 IL 编译的程序集和 .NET Core &沙马林的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Updated with a solution that works for me. See the bottom of this question.

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

Context:
I needed a way to evaluate the size of a generic type for the purpose of calculating array lengths to fit within a certain byte size. Basically, sizeof similar to what C/C++ provides.

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

C#'s sizeof and Marshal.SizeOf are not suitable for this, because of their many limitations.

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

With this in mind, I wrote an assembly in IL that enables the functionality I was looking for through the sizeof opcode. I'm aware that it essentially evaluates to IntPtr.Size with reference types.

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

I duplicated this for .NET Standard & Core, referencing what I believed were the correct equivalents of mscorlib. Note that the IL compiles fine, this question is about another issue.

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

Code:
Headers per target framework:

.NET: (WindowsMicrosoft.NETFrameworkv4.0.30319ilasm.exe)

.NET: (WindowsMicrosoft.NETFrameworkv4.0.30319ilasm.exe)

.assembly extern mscorlib {}

.NET Standard: (ilasm 提取自 nuget)

.NET Standard: (ilasm extracted from nuget)

.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,但我已经对两者进行了测试)

.NET Core: (same ilasm as standard, though I've tested with both)

.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 应用程序引用时,我收到以下错误:

Problem:
When any dll compiled in this manner is referenced by a .NET Core or Xamarin application, I receive the following error:

对象"类型是在未引用的程序集中定义的.您必须添加对程序集System.Runtime, Version=0.0.0.0"的引用,文化=中性,PublicKeyToken=null'.

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 项目引用.

This issue doesn't occur when such dlls are referenced by .NET projects or .NET standard libraries which are then referenced by a .NET project.

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

I've read countless articles, posts, and repositories detailing this error with different versions and assemblies. The typical solution seems to be to add an explicit reference to the target framework's equivalent of mscorlib(breaking portability). There seems to be a lack of information about using IL compiled assemblies for .NET Standard & Core.

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

To my understanding, .NET Standard & Core use facades that forward type definitions so they may be resolved by the target framework's runtime, enabling portability.

我尝试了以下方法:

  • System.Runtime 的显式版本
  • 反汇编从 C# 编译的 .NET Core 库,使用完全相同的引用.奇怪的是,它们似乎针对显式版本(例如 .NET Core 2.0 中的 System.Runtime 4.2).
  • 使用Emit API.编译到内存似乎可行,但不是一个选项,因为我还针对 Xamarin.iOS(仅限 AOT).从磁盘引用此类动态编译的程序集会导致与我手动编译它们相同的错误.
  • Explicit versions of System.Runtime
  • Disassembling .NET Core libraries compiled from C#, using the exact same references. Oddly enough they seem to target explicit versions(such as System.Runtime 4.2 in .NET Core 2.0).
  • Generating the code dynamically using the Emit API. Compiling to memory appears to work, but isn't an option because I'm also targeting Xamarin.iOS(AOT only). Referencing such dynamically compiled assemblies from disk results in the same error as if I compiled them manually.

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

Update:
I attempted the solution in Jacek's answer(following the build instructions here), yet couldn't configure my system to compile corefx with the build script or VS 2017. However, after digging through the code of System.Runtime.CompilerServices.Unsafe I discovered a solution.

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

This probably seems obvious, but I was referencing the wrong version of System.Runtime.

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

Headers per target framework(copied from 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).

In all source files, use CORELIB to reference types in mscorlib(i.e. [CORELIB]System.Object).

推荐答案

在 DotNet CoreFX 存储库中有一个很好的示例,说明如何正确执行此操作.System.Runtime.CompilerServices.Unsafe 是仅 IL 的程序集,可供 .NET Core 和 Xamarin 使用.

There is a very good example of how to do it correctly in the DotNet CoreFX repo. System.Runtime.CompilerServices.Unsafe is an IL only assembly and can be used by .NET Core and 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 的程序集的最快方法,可以保证在所有目标上正常工作.

There are two approaches to the problem: (i) try to recreate required elements of the build configuration in your project from scratch - what will be time consuming and very involved - corefx build system is really complex, (ii) use existing build infrastructure and create your IL project inside .NET Core CoreFX repo by replicating System.Runtime.CompilerServices.Unsafe project, changing naming and and replacing IL code with yours. In my opinion this is the fastest way to build IL based assembly which will be guaranteed to work properly with all targets.

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

To build your assembly while targeting any particular version of .NET Core or .NET Standard just create it in the release branches: release/2.0.0, release/1.1.0 etc.

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

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 应该会抑制它:

There is an option to try for suppressing that compilation error alone in a project referencing assembly that triggers it. Putting the following property to a new format csproj/vbproj should suppress it:

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

这篇关于使用从 IL 编译的程序集和 .NET Core &amp;沙马林的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆