设置
我正在处理一个没有外部依赖的C#项目,名为"Hopper“。我把它分成两个不同的模块,每个模块都包含在相应的子文件夹中。与此有关的问题如下:
Utils/Hopper.Utils.csprojShared/Hopper.Shared.csprojCore/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项目包含一些类型,包括类和结构。一些源文件是由工具自动生成的,并在顶部包含#pragma warning disable。(移除它没有帮助)。
Hopper.Core引用的两个项目只包含Core使用的一些类型和属性,因此与问题无关。它们不引用任何其他项目或dll。
问题所在
我能够通过在带有项目文件的子文件夹中运行Hopper.Mine来编译dotnet build。我能够在每个项目的文件夹下看到bin/Debug/net4.8/Hopper.Whatever.dll中引用的子项目的输出dll。VSCode中的Intellisense也正确工作,不报告任何错误。
Hopper.Mine有一个包含主函数的类,它是为测试而创建的。它列出了已成功加载的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需要注意的有四件事:
IStat派生。Hopper.Shared或Hopper.Utils中的任何类型。我非常确信来自Hopper.Core输出文件夹的dll输出与Hopper.Mine输出文件夹中的dll输出完全相同,我也非常确定当我启动程序时,正确的dll是针对的(我已经尝试用VS进行调试,在这里我可以看到哪个dll被链接到哪个dll上,以及匹配的路径)。
我尝试过删除AssemblyName属性,将csproj文件从Hopper.Whatever.csproj重命名为Hopper_Whatever.csproj,我显然尝试过清理以前的任何输出dll并从头构建,这些都没有帮助。我还能在不同的机器上复制这个结果,结果完全一样。
我已经尝试使用反编译程序ILSpy查看ILSpy dll。我能够看到“缺失”类型,因为它们在代码中。

我在谷歌上搜索了几个小时,一直在寻找像我这样的问题,但没有结果。我发现很多人设置项目文件不正确,因此他们意外地链接到不同版本的dll而不是他们所预期的。对于一些人来说,这种依赖性出现在一个被引用的第三方项目的需求中。一些人指责GAC提供了不正确的dll版本。但是,我非常肯定我的项目是正确设置的(+设置非常简单,没有引用第三方项目),而且我引用的是正确的dll(因为我可以在其中看到我的类型)。看起来这些类型在dll中是“声明的”,而不是“已定义的”(某种程度上),所以dll被破坏了。但是为什么反编译不会失败呢?所以我现在完全被困在这个问题上了。
手动加载程序集
我试着用它们的名字动态地链接dll。列出从Hopper.Shared和Hopper.Utils导入的类型是有效的,但是,当我尝试使用Hopper.Core时,得到的输出与上面的示例相同。
为此,我将以前的dll从输出复制到一个新文件夹,从dotnet build中删除引用,用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链接。为了运行它,您需要:
Meta中运行项目才能生成它。只需在dotnet run文件夹中运行Meta。Mine子项目了。发布于 2021-05-01 10:20:35
找出原因。CLR类型系统中有一个bug已经存在6年了。基本上,在其他结构中声明的泛型结构类型的字段(无论是静态的还是非静态的)都会导致这种行为。有关详细信息,请参阅此github线程。
在我的例子中,我已经能够制作一个更短的版本来说明这个错误。
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从struct更改为class works:
// 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];
}https://stackoverflow.com/questions/67336326
复制相似问题