首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >某些类型无法从从本地项目编译的dll加载。

某些类型无法从从本地项目编译的dll加载。
EN

Stack Overflow用户
提问于 2021-04-30 15:13:29
回答 1查看 216关注 0票数 1

设置

我正在处理一个没有外部依赖的C#项目,名为"Hopper“。我把它分成两个不同的模块,每个模块都包含在相应的子文件夹中。与此有关的问题如下:

  • Utils/Hopper.Utils.csproj
  • Shared/Hopper.Shared.csproj
  • Core/Hopper.Core.csproj参考了UtilsShared,稍后将提供更多详细信息。
  • Mine/Hopper.Mine.csproj,它引用了Core

所有这些目标都是.NET 4.8,包括Hopper.Core

我已经将每个项目的AssemblyName属性设置为相应的字符串。例如,Hopper.Utils.csproj

代码语言:javascript
运行
复制
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Library</OutputType>
    <TargetFramework>net4.8</TargetFramework>
    <AssemblyName>Hopper.Utils</AssemblyName>
  </PropertyGroup>

</Project>

Hopper.Core.csproj通过ProjectReference引用其他项目,如下所示:

代码语言:javascript
运行
复制
<ItemGroup>
  <ProjectReference Include="..\Utils\Hopper.Utils.csproj" />
  <ProjectReference Include="..\Shared\Hopper.Shared.csproj" />
</ItemGroup>

为测试目的创建的Hopper.Mine.csproj引用如下所示:

代码语言:javascript
运行
复制
<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类型,并报告了尚未加载的类型:

代码语言:javascript
运行
复制
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);
            }
        }
    }
}

运行此代码后,我可以看到一堆问题类型,其中一些类型重复,其余类型已成功加载:

代码语言:javascript
运行
复制
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

需要注意的有四件事:

  1. 未能加载的类型都是结构,并从IStat派生。
  2. 具有这些类型的源文件是由工具自动生成的(由工具加载生成的其他文件没有错误)。
  3. 这些类型不引用Hopper.SharedHopper.Utils中的任何类型。
  4. 有些类型具有嵌套的结构,深度可达3层,有些更深的类型似乎已成功加载。

我非常确信来自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.SharedHopper.Utils导入的类型是有效的,但是,当我尝试使用Hopper.Core时,得到的输出与上面的示例相同。

为此,我将以前的dll从输出复制到一个新文件夹,从dotnet build中删除引用,用dotnet build编译新代码,然后将可执行文件移动到新文件夹,最后在控制台中运行它。守则:

代码语言:javascript
运行
复制
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链接。为了运行它,您需要:

  1. 安装.NET 4.8。
  2. 从上面的链接克隆存储库。
  3. 有些代码还没找到。您需要在文件夹Meta中运行项目才能生成它。只需在dotnet run文件夹中运行Meta
  4. 现在您可以运行Mine子项目了。
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-01 10:20:35

找出原因。CLR类型系统中有一个bug已经存在6年了。基本上,在其他结构中声明的泛型结构类型的字段(无论是静态的还是非静态的)都会导致这种行为。有关详细信息,请参阅此github线程

在我的例子中,我已经能够制作一个更短的版本来说明这个错误。

代码语言:javascript
运行
复制
public struct Index<T>
{
}

public struct Test
{
    public static Index<Test> Index;
}

使用以下代码测试程序:

代码语言:javascript
运行
复制
public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine(typeof(Test).Assembly.GetTypes());
    }
}

运行代码时,将引发以下错误:

代码语言:javascript
运行
复制
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:

代码语言:javascript
运行
复制
// This works!
public struct Index<T>
{
}

public class Test
{
    public static Index<Test> Index;
}

在某些情况下,这可能是不可接受的。因此,下面是另一个解决办法--将静态字段存储在数组中并使用属性访问它也是可行的:

代码语言:javascript
运行
复制
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];
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67336326

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档