无法从从本地项目编译的 dll 加载某些类型
Some types could not be loaded from a dll that was compiled from a local project
设置
我正在开发一个没有外部依赖项的 C# 项目,名为“Hopper”。我将它分成几个不同的模块,每个模块都包含在相应的子文件夹中。该问题的相关内容如下:
Utils/Hopper.Utils.csproj
Shared/Hopper.Shared.csproj
Core/Hopper.Core.csproj
,其中引用了 Utils
和 Shared
,稍后会提供更多详细信息
Mine/Hopper.Mine.csproj
,其中引用了 Core
所有这些都针对 .NET 4.8,包括 Hopper.Core
。
我已将每个项目的 AssemblyName
属性 设置为相应的字符串。例如,Hopper.Utils.csproj
:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net4.8</TargetFramework>
<AssemblyName>Hopper.Utils</AssemblyName>
</PropertyGroup>
</Project>
Hopper.Core.csproj
通过 ProjectReference
引用其他项目,像这样:
<ItemGroup>
<ProjectReference Include="..\Utils\Hopper.Utils.csproj" />
<ProjectReference Include="..\Shared\Hopper.Shared.csproj" />
</ItemGroup>
Hopper.Mine.csproj
,为测试目的而创建,像这样引用核心:
<ItemGroup>
<ProjectReference Include="..\Core\Hopper.Core.csproj" />
</ItemGroup>
Hopper.Core
项目包含一些类型,包括 classes 和结构。一些源文件由工具自动生成,并在顶部包含 #pragma warning disable
。 (删除它没有帮助)。
Hopper.Core
引用的两个项目只包含一些类型和属性,被Core
使用,所以与问题无关。它们不引用任何其他项目或 dll。
问题
我可以通过 运行ning dotnet build
在项目文件的子文件夹中编译 Hopper.Mine
。我能够在每个项目文件夹下的 bin/Debug/net4.8/Hopper.Whatever.dll
中看到引用子项目的输出 dll。 VSCode 中的 Intellisense 也能正常工作,不会报告任何错误。
Hopper.Mine
有一个带有主函数的 class,是为测试而创建的。它列出了加载成功的Hopper.Core
类型,并报告了未加载的类型:
using System;
using System.Reflection;
using System.Text;
namespace Hopper.Mine
{
public class Program
{
public static void Main(string[] args)
{
Assembly lib = typeof(Hopper.Core.Action).Assembly;
Type[] types;
try
{
types = lib.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
Console.WriteLine(exSub.Message);
}
types = ex.Types;
}
foreach (Type type in types)
{
if (type != null)
Console.WriteLine(type.FullName);
}
}
}
}
运行这段代码,我可以看到一堆有问题的类型,其中一些重复,其余类型加载成功:
Could not load type 'Hopper.Core.Stat.Attack' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Dig' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Move' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Push' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Attack' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Attack' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
... more errors here ...
Hopper.Core.DirectedPredict
Hopper.Core.UndirectedPredict
Hopper.Core.DirectedDo
... more types here ...
Hopper.Core.Stat.Attack+Source+Resistance
Hopper.Core.Stat.Push+Source+Resistance
需要注意的4件事:
- 加载失败的类型都是结构体,派生自
IStat
。
- 这些类型的源文件已经由工具自动生成(其他文件由工具加载生成,没有错误)。
- 这些类型没有引用
Hopper.Shared
或 Hopper.Utils
中的任何类型。
- 有些类型有嵌套结构,最多 3 级深,一些更深的似乎已成功加载。
我很确定 Hopper.Core
输出文件夹中的 dll 输出与 Hopper.Mine
输出文件夹中的相同。我也很确定当我启动程序时正确的 dll 被 linked 反对(我试过用 VS 调试,在那里我可以看到哪个 dll 被 linked 反对并且路径匹配).
我已经尝试删除 AssemblyName
属性,将 csproj 文件从 Hopper.Whatever.csproj
重命名为 Hopper_Whatever.csproj
,我显然已经尝试清理所有以前的输出 dll 并从头开始构建,none 其中有帮助。我也已经能够在不同的机器上重现它并得到完全相同的结果。
我尝试使用反编译器 ILSpy 查看 Hopper.Core
dll。我能够看到代码中的“缺失”类型。
我已经在 google 上搜索了几个小时,但没有找到像我这样的问题。我发现很多人错误地设置了他们的项目文件,因此他们意外地 link 针对与他们预期不同的 dll 版本。对于某些人来说,这种依赖性出现在所引用的第三方项目之一的需求中。有些人指责 GAC 提供了错误版本的 dll。但是,我很确定我的项目设置正确(+ 设置非常简单,没有引用第三方项目)并且我引用了正确的 dll(因为我可以在其中看到我的类型)。看起来 dll 中的类型是 'declared',但不是 'defined'(某种程度上),因此 dll 已损坏。但是为什么反编译不会失败呢?所以我目前完全坚持这一点。
正在手动加载程序集
我已经尝试 link 通过 dll 的名称动态地对付它们。列出从 Hopper.Shared
和 Hopper.Utils
导入的类型是可行的,但是,当我使用 Hopper.Core
尝试它时,我得到与上面示例相同的输出。
为了实现这一点,我将之前的 dll 从输出复制到一个新文件夹,从 Hopper.Mine
中删除了引用,用 dotnet build
编译了新代码,然后移动了可执行文件到新文件夹,最后在控制台中 运行 它。代码:
using System;
using System.Reflection;
using System.Text;
namespace Hopper.Mine
{
public class Program
{
public static void Main(string[] args)
{
Assembly shared = Assembly.LoadFrom("Hopper.Shared.dll");
Assembly utils = Assembly.LoadFrom("Hopper.Utils.dll");
Assembly core = Assembly.LoadFrom("Hopper.Core.dll");
Type[] types;
try
{
types = core.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
Console.WriteLine(exSub.Message);
}
types = ex.Types;
}
foreach (Type type in types)
{
if (type != null)
Console.WriteLine(type.FullName);
}
}
}
}
重现
如果有帮助,您可以尝试 运行我的代码。这是 github link to the current state of the project。为了运行它,你需要:
- .NET 4.8 安装。
- 从上面的 link 克隆存储库。
- 仍然缺少一些代码。您需要 运行 文件夹
Meta
中的项目来生成它。 运行 dotnet run
在 Meta
文件夹中。
- 现在您可以 运行
Mine
子项目。
找到原因了。 CLR 类型系统中有一个错误已经存在了 6 年。基本上,在其他结构中声明的通用结构类型的字段(静态或非静态)会导致此行为。 See this github thread for more details.
就我而言,我已经能够制作一个更短的版本来说明错误。
public struct Index<T>
{
}
public struct Test
{
public static Index<Test> Index;
}
使用以下代码测试程序:
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine(typeof(Test).Assembly.GetTypes());
}
}
当代码为运行时,抛出如下错误:
Unhandled Exception: System.TypeLoadException: Could not load type 'Hopper.Mine.Test' from assembly 'Hopper.Mine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
at Hopper.Mine.Program.Main(String[] args)
但是,有一种简单的解决方法。将 Test
从结构更改为 class 有效:
// This works!
public struct Index<T>
{
}
public class Test
{
public static Index<Test> Index;
}
在某些情况下,这可能是不可接受的。所以这是另一个解决方法——将静态字段存储在数组中并使用 属性 访问它也有效:
public struct Index<T>
{
}
public struct Test
{
public static Index<Test> Index { get => index[0]; set => index[0] = value; }
public static Index<Test>[] index = new Index<Test>[1];
}
设置
我正在开发一个没有外部依赖项的 C# 项目,名为“Hopper”。我将它分成几个不同的模块,每个模块都包含在相应的子文件夹中。该问题的相关内容如下:
Utils/Hopper.Utils.csproj
Shared/Hopper.Shared.csproj
Core/Hopper.Core.csproj
,其中引用了Utils
和Shared
,稍后会提供更多详细信息Mine/Hopper.Mine.csproj
,其中引用了Core
所有这些都针对 .NET 4.8,包括 Hopper.Core
。
我已将每个项目的 AssemblyName
属性 设置为相应的字符串。例如,Hopper.Utils.csproj
:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net4.8</TargetFramework>
<AssemblyName>Hopper.Utils</AssemblyName>
</PropertyGroup>
</Project>
Hopper.Core.csproj
通过 ProjectReference
引用其他项目,像这样:
<ItemGroup>
<ProjectReference Include="..\Utils\Hopper.Utils.csproj" />
<ProjectReference Include="..\Shared\Hopper.Shared.csproj" />
</ItemGroup>
Hopper.Mine.csproj
,为测试目的而创建,像这样引用核心:
<ItemGroup>
<ProjectReference Include="..\Core\Hopper.Core.csproj" />
</ItemGroup>
Hopper.Core
项目包含一些类型,包括 classes 和结构。一些源文件由工具自动生成,并在顶部包含 #pragma warning disable
。 (删除它没有帮助)。
Hopper.Core
引用的两个项目只包含一些类型和属性,被Core
使用,所以与问题无关。它们不引用任何其他项目或 dll。
问题
我可以通过 运行ning dotnet build
在项目文件的子文件夹中编译 Hopper.Mine
。我能够在每个项目文件夹下的 bin/Debug/net4.8/Hopper.Whatever.dll
中看到引用子项目的输出 dll。 VSCode 中的 Intellisense 也能正常工作,不会报告任何错误。
Hopper.Mine
有一个带有主函数的 class,是为测试而创建的。它列出了加载成功的Hopper.Core
类型,并报告了未加载的类型:
using System;
using System.Reflection;
using System.Text;
namespace Hopper.Mine
{
public class Program
{
public static void Main(string[] args)
{
Assembly lib = typeof(Hopper.Core.Action).Assembly;
Type[] types;
try
{
types = lib.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
Console.WriteLine(exSub.Message);
}
types = ex.Types;
}
foreach (Type type in types)
{
if (type != null)
Console.WriteLine(type.FullName);
}
}
}
}
运行这段代码,我可以看到一堆有问题的类型,其中一些重复,其余类型加载成功:
Could not load type 'Hopper.Core.Stat.Attack' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Dig' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Move' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Push' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Attack' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Attack' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
... more errors here ...
Hopper.Core.DirectedPredict
Hopper.Core.UndirectedPredict
Hopper.Core.DirectedDo
... more types here ...
Hopper.Core.Stat.Attack+Source+Resistance
Hopper.Core.Stat.Push+Source+Resistance
需要注意的4件事:
- 加载失败的类型都是结构体,派生自
IStat
。 - 这些类型的源文件已经由工具自动生成(其他文件由工具加载生成,没有错误)。
- 这些类型没有引用
Hopper.Shared
或Hopper.Utils
中的任何类型。 - 有些类型有嵌套结构,最多 3 级深,一些更深的似乎已成功加载。
我很确定 Hopper.Core
输出文件夹中的 dll 输出与 Hopper.Mine
输出文件夹中的相同。我也很确定当我启动程序时正确的 dll 被 linked 反对(我试过用 VS 调试,在那里我可以看到哪个 dll 被 linked 反对并且路径匹配).
我已经尝试删除 AssemblyName
属性,将 csproj 文件从 Hopper.Whatever.csproj
重命名为 Hopper_Whatever.csproj
,我显然已经尝试清理所有以前的输出 dll 并从头开始构建,none 其中有帮助。我也已经能够在不同的机器上重现它并得到完全相同的结果。
我尝试使用反编译器 ILSpy 查看 Hopper.Core
dll。我能够看到代码中的“缺失”类型。
我已经在 google 上搜索了几个小时,但没有找到像我这样的问题。我发现很多人错误地设置了他们的项目文件,因此他们意外地 link 针对与他们预期不同的 dll 版本。对于某些人来说,这种依赖性出现在所引用的第三方项目之一的需求中。有些人指责 GAC 提供了错误版本的 dll。但是,我很确定我的项目设置正确(+ 设置非常简单,没有引用第三方项目)并且我引用了正确的 dll(因为我可以在其中看到我的类型)。看起来 dll 中的类型是 'declared',但不是 'defined'(某种程度上),因此 dll 已损坏。但是为什么反编译不会失败呢?所以我目前完全坚持这一点。
正在手动加载程序集
我已经尝试 link 通过 dll 的名称动态地对付它们。列出从 Hopper.Shared
和 Hopper.Utils
导入的类型是可行的,但是,当我使用 Hopper.Core
尝试它时,我得到与上面示例相同的输出。
为了实现这一点,我将之前的 dll 从输出复制到一个新文件夹,从 Hopper.Mine
中删除了引用,用 dotnet build
编译了新代码,然后移动了可执行文件到新文件夹,最后在控制台中 运行 它。代码:
using System;
using System.Reflection;
using System.Text;
namespace Hopper.Mine
{
public class Program
{
public static void Main(string[] args)
{
Assembly shared = Assembly.LoadFrom("Hopper.Shared.dll");
Assembly utils = Assembly.LoadFrom("Hopper.Utils.dll");
Assembly core = Assembly.LoadFrom("Hopper.Core.dll");
Type[] types;
try
{
types = core.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
Console.WriteLine(exSub.Message);
}
types = ex.Types;
}
foreach (Type type in types)
{
if (type != null)
Console.WriteLine(type.FullName);
}
}
}
}
重现
如果有帮助,您可以尝试 运行我的代码。这是 github link to the current state of the project。为了运行它,你需要:
- .NET 4.8 安装。
- 从上面的 link 克隆存储库。
- 仍然缺少一些代码。您需要 运行 文件夹
Meta
中的项目来生成它。 运行dotnet run
在Meta
文件夹中。 - 现在您可以 运行
Mine
子项目。
找到原因了。 CLR 类型系统中有一个错误已经存在了 6 年。基本上,在其他结构中声明的通用结构类型的字段(静态或非静态)会导致此行为。 See this github thread for more details.
就我而言,我已经能够制作一个更短的版本来说明错误。
public struct Index<T>
{
}
public struct Test
{
public static Index<Test> Index;
}
使用以下代码测试程序:
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine(typeof(Test).Assembly.GetTypes());
}
}
当代码为运行时,抛出如下错误:
Unhandled Exception: System.TypeLoadException: Could not load type 'Hopper.Mine.Test' from assembly 'Hopper.Mine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
at Hopper.Mine.Program.Main(String[] args)
但是,有一种简单的解决方法。将 Test
从结构更改为 class 有效:
// This works!
public struct Index<T>
{
}
public class Test
{
public static Index<Test> Index;
}
在某些情况下,这可能是不可接受的。所以这是另一个解决方法——将静态字段存储在数组中并使用 属性 访问它也有效:
public struct Index<T>
{
}
public struct Test
{
public static Index<Test> Index { get => index[0]; set => index[0] = value; }
public static Index<Test>[] index = new Index<Test>[1];
}