C# 中类型如何既相等又不相等? (.NET 8.0、Linux、自定义 .NET 主机)

问题描述 投票:0回答:1

操作系统:Ubuntu 24.04 Linux 框架:.NET 8.0.10 IDE:Rider(最新)

我正在从本机 Linux 项目创建自定义 .NET 主机来运行托管代码。

问题是我在大约 50% 的情况下比较类型时遇到逻辑矛盾。该问题 100% 可重现,并且仅发生在 Linux 上且使用自定义 .NET 主机。

它还会影响

GetCustomAttributes
方法的执行,在这种情况下找不到属性。调试使我看到了屏幕截图中可见的情况,其中类型不匹配,但当我在调试器中比较它们时(参见 watch 变量),它们确实匹配。

enter image description here

如果我直接将托管代码作为控制台可执行文件运行,而不需要自定义 .NET 主机,则完全相同的代码可以很好地工作(可以匹配所有类型)。

我按照 Microsoft 的说明编写了自定义 .NET 主机: https://learn.microsoft.com/en-us/dotnet/core/tutorials/netcore-hosting

除了上述问题之外,.NET Host 运行良好,可以加载所有程序集,并且所有其他代码似乎运行良好。

  • 这不是调试器问题,因为该问题也会影响 GetCustomAttributes(标准库方法)。
  • 我已经检查了所有项目和依赖项,它们都很好。所有项目上的 Nullable 均已关闭,并选择了 C# 11。我只有图书馆项目。
  • 生成的 IL 代码与人们所期望的一致。
  • 更新:事实证明是错误的:没有程序集被加载两次。

我在这里想念什么? .NET 主机配置错误?以某种方式加载 Typo 对象两次? CLR 或 JIT 错误?

更新:

  • 我检查了两个类型对象上的装配位置,它们是完全相同的。

  • 这是类型确实不同的提示:

type.GetHashCode() = {int} 18014707
stopAtType.GetHashCode() = {int} 59387592
Loaded Assembly '/home/viktor/se-dotnet-server-local/SpaceEngineersDedicated/bin/Debug/net8.0/Sandbox.Game.dll'
Loading module /home/viktor/se-dotnet-server-local/SpaceEngineersDedicated/bin/Debug/net8.0/Sandbox.Game.dll in application domain 1:clr_libhost
Symbols for module /home/viktor/se-dotnet-server-local/SpaceEngineersDedicated/bin/Debug/net8.0/Sandbox.Game.dll loaded
...
Loaded Assembly '/home/viktor/se-dotnet-server-local/SpaceEngineersDedicated/bin/Debug/net8.0/Sandbox.Game.dll'
Loading module /home/viktor/se-dotnet-server-local/SpaceEngineersDedicated/bin/Debug/net8.0/Sandbox.Game.dll in application domain 1:clr_libhost
Symbols for module /home/viktor/se-dotnet-server-local/SpaceEngineersDedicated/bin/Debug/net8.0/Sandbox.Game.dll loaded

AssemblyLoadContext
顶部打印
Main
实例:

foreach (var assemblyLoadContext in AssemblyLoadContext.All)
{
    Console.WriteLine($"AssemblyLoadContext: {assemblyLoadContext.Name}");
}
Console.WriteLine($"AssemblyLoadContext.Default: {AssemblyLoadContext.Default.Name}");

直接运行(无需自定义.NET Host):

AssemblyLoadContext: Default
AssemblyLoadContext.Default: Default

通过自定义 .NET 主机运行:

AssemblyLoadContext: IsolatedComponentLoadContext(/home/viktor/se-dotnet-server-local/SpaceEngineersDedicated/bin/Debug/net8.0/SpaceEngineersDedicated.dll)
AssemblyLoadContext: Default
AssemblyLoadContext.Default: Default

它应该能解释问题。

代码

相关 .NET Host C++ 代码的要点:https://gist.github.com/viktor-ferenczi/feff098dfe27bba0f03929a89e2994a4

使用的运行时配置的要点:https://gist.github.com/viktor-ferenczi/9ba57aa40510ccd928e80570ddd54983

使用适当的值调用

RunManagedCode
函数。托管代码启动,因此传递的字符串是好的。

(该项目的其余部分现在一团糟,编译起来非常复杂,所以它不是公开的,但如果我成功的话,最终会公开的。)

c# .net linux
1个回答
0
投票

您几乎发现了发生了什么(双加载程序集),但我可以从概念上解释它。

看我的评论:

System.Type.FullName
不能是类型的身份;它从来不是为此目的而设计的。它只不过是一个完全限定的名称。但是,不同的类型可以具有相同的命名空间和相同的名称。要查看它,请考虑以下示例:

组装1:

namespace NameMatch {
    public class Demo { }
    public class AssemblyIndicator1 { }
}

组装2:

namespace NameMatch {
    public class Demo { }
    public class AssemblyIndicator2 { }
}

现在,创建另一个程序集并引用 Assembly1 和 Assembly2。这是可能的。您甚至可以使用两个相同的类型

NameMatch.Demo
,但不能直接使用:

var demo = new NameMatch.Demo();
// compilation fails:
// The type 'Demo' exists in both 'doubleLoaded1,
// Version=2.1.0.0,
// Culture=neutral, PublicKeyToken=null'
// and 'doubleLoaded2, Version=2.1.0.0, Culture=neutral, 
// PublicKeyToken=null'...

以下是如何使用反射检索引用两个程序集的程序集中的类型:

 System.Reflection.Assembly assembly1 = 
     typeof(NameMatch.AssemblyIndicator1).Assembly;
 System.Reflection.Assembly assembly2 = 
     typeof(NameMatch.AssemblyIndicator2).Assembly;
 System.Type Type1 = assembly1.GetType("NameMatch.Demo");
 System.Type Type2 = assembly2.GetType("NameMatch.Demo");

显然,

bool theSame = Type1 == Type2
给你
false
,而
Type1.FullName
Type2.FullName
都返回
"NameMatch.Demo"

您甚至可以使用反射实例化

Type1
Type2
。但类型不同。

© www.soinside.com 2019 - 2024. All rights reserved.