Updated with a solution that works for me. See the bottom of this question.
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#'s sizeof
and Marshal.SizeOf are not suitable for this, because of their many limitations.
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.
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: (Windows\Microsoft.NET\Framework\v4.0.30319\ilasm.exe)
.assembly extern mscorlib {}
.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: (same ilasm as standard, though I've tested with both)
.assembly extern System.Runtime
{
.ver 0:0:0:0
}
Source:
.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
}
}
Problem:
When any dll compiled in this manner is referenced by a .NET Core or Xamarin application, I receive the following error:
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'.
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.
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.
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.
I've tried the following:
- 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.
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.
This probably seems obvious, but I was referencing the wrong version of System.Runtime
.
Headers per target framework(copied from corefx):
.NET:
#define CORELIB "mscorlib"
.assembly extern CORELIB {}
.NET Standard:
#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
}
In all source files, use CORELIB
to reference types in mscorlib(i.e. [CORELIB]System.Object
).
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
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.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.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:
Ok, this is the result of my research (based on https://github.com/dotnet/corefx/tree/master/src/System.Runtime.CompilerServices.Unsafe and https://github.com/jvbsl/ILProj):
global.json
:ILProj\ILProj.ilproj
:ILProj\include\netstandard\coreassembly.h
:ILProj\Class.il
If you have difficulties to put it all together, then look at the example here: https://github.com/Konard/ILProj