【发布时间】:2021-04-30 15:13:29
【问题描述】:
设置
我正在开发一个没有外部依赖项的 C# 项目,名为“Hopper”。我将它分成几个不同的模块,每个模块都包含在相应的子文件夹中。该问题的相关问题如下:
Utils/Hopper.Utils.csprojShared/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 项目包含一些类型,包括类和结构。一些源文件已由工具自动生成,并在顶部包含#pragma warning disable。 (删除它没有帮助)。
Hopper.Core 引用的两个项目只包含Core 使用的一些类型和属性,因此对问题没有任何意义。它们不引用任何其他项目或 dll。
问题
我可以通过在项目文件的子文件夹中运行dotnet build 来编译Hopper.Mine。我可以在每个项目文件夹下的bin/Debug/net4.8/Hopper.Whatever.dll 中看到引用子项目的输出 dll。 VSCode 中的 Intellisense 也能正常工作,不会报任何错误。
Hopper.Mine 有一个带有 Main 函数的类,用于测试。它列出了已成功加载的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(我尝试使用 VS 进行调试,在其中我可以看到正在链接的 dll 以及匹配的路径)。
我尝试删除AssemblyName 属性,将csproj 文件从Hopper.Whatever.csproj 重命名为Hopper_Whatever.csproj,我显然尝试清理任何以前的输出dll 并从头开始构建,但这些都没有帮助。我还能够在另一台机器上以完全相同的结果重现这一点。
我尝试使用反编译器 ILSpy 查看Hopper.Core dll。我能够看到代码中的“缺失”类型。
我已经在谷歌上搜索了几个小时来寻找像我这样的问题,但无济于事。我发现很多人错误地设置了他们的项目文件,因此他们意外地链接到了与他们预期不同的 dll 版本。对于某些人来说,这种依赖关系出现在引用的第三方项目之一的需求中。有些人指责 GAC 提供了不正确的 dll 版本。但是,我很确定我的项目设置正确(+ 设置非常简单,并且没有引用第三方项目)并且我引用了正确的 dll(因为我可以在其中看到我的类型)。看起来类型是在 dll 中“声明”的,但不是“定义”(有点),所以 dll 被损坏了。但是为什么反编译不会失败呢?所以我目前完全坚持这一点。
手动加载程序集
我已尝试通过名称动态链接 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 已安装。
- 从上面的链接克隆存储库。
- 仍然缺少一些代码。您需要运行文件夹
Meta中的项目来生成它。只需在Meta文件夹中运行dotnet run。 - 现在您可以运行
Mine子项目。
【问题讨论】:
-
Core 项目应该使用 Net 4.8 构建,然后所有项目都应该针对您想要的 Core 版本。在过去的两周里,我在目标 Core 3.1 上看到了很多类似的问题。请参阅以下内容:stackoverflow.com/questions/67316994/…
-
@jdweng,不过,我的目标不是 Core 3.1。我的问题中提到的所有项目都只针对 .NET 4.8。
-
Core/Hopper.Core.csproj 怎么样,它引用了 Utils 和 Shared,稍后会详细介绍。是否需要重新编译为 Net 4.8?
-
@jdweng,所有提到的项目都将
TargetFramework设置为net4.8,就像Hopper.Utils.csproj的示例一样。Core未命名为Core,因为它针对 dotnet Core,如果这就是您的意思。 -
你安装了dotnet Core吗?