【问题标题】:How to check in code, which assemblies are referenced but not loaded? (.NET 6.0)如何签入代码,哪些程序集被引用但未加载? (.NET 6.0)
【发布时间】:2021-12-03 17:56:51
【问题描述】:

更新:

这个问题比我想象的要难解释。

  1. 我有 2 个项目。 (.NET 6.0)
  2. 每个项目都是一个 NuGet 包。
  3. 包不能相互依赖。 (每一个都必须能够单独使用。)
  4. 如果包B 检测到包A 引用(我的意思是在.csproj 文件中使用<ProjectReference><Package Reference> 标记 - 它应该加载包A 并执行一种方法。

现在我已经做到了:

(包B代码)

try {
    var assembly = Assembly.Load("A");
    var type = assembly.GetTypes.FirstOrDefault(t => t.Name == "MyKnownType");
    var instance = (IApiOfA)Activator.CreateInstance(t);
    instance.MethodOfA(someDataIHaveInB);
}
catch { }

我讨厌滥用异常。我的问题是如何跳过异常部分。

查询AppDomain.CurrentDomain 无济于事——它只会获取已加载的程序集。 Assembly.GetEntryAssembly().GetReferencedAssemblies() 也不起作用,当我显式加载它时,它甚至不显示我的目标程序集。 AppDomain.CurrentDomain.Evidence 在 .NET 6.0 中不可用。

有什么线索吗?

另一个更新: 这个:Check if Assembly Exists by name before loading it

正如其他人所说:异常并不总是那么糟糕。也许它们不是最优雅的方式,但有一个权衡。一项简单的检查将在应用程序开发中节省大量时间。软件包的作用很容易被忽略,并导致浪费大量时间来弄清楚为什么它不起作用。现在,当 A 和 B 包含在项目中时,将始终进行正确的交互,并且两者都不会失败。如果只有其中一个包 - 没有问题,则问题不存在。如果两者都使用,它们会合作。所以我只是在寻找一个微优化:)

【问题讨论】:

  • “引用”有两种定义,不清楚您指的是哪一种:(a) 项目构建系统中的引用导致将 /r: 参数传递给 C# 编译器或 (b) 用法在来自其他 DLL 的 MDTOKEN 值的编译程序集中。
  • 特别是“.NET 自动加载程序集,当它被我的项目文件引用时”绝对不是真的。当您调用包含来自其他程序集的 MDTOKEN 的方法时,或者当您加载其成员签名使用来自其他程序集的类型的类型时,会自动加载依赖项。 JIT 编译和加载依赖非常懒惰。
  • @BenVoight:当然,我使用了错误的措辞,通过引用我的意思是通过执行方法或属性来使用类型。然后加载程序集,确实,它是惰性的。
  • @RyanWilson:不,它没有回答我的问题。 .NET 6.0 没有 Evidence 属性。
  • @BenVoigt:我的意思是在.csproj 文件中引用PackageReferenceProjectReference 标签。我必须澄清 - .NET 6.0。

标签: c# .net-6.0


【解决方案1】:

您将需要一个自定义构建步骤,将所有引用列表嵌入到生成的可执行文件中。 Microsoft 工具不会,它们只为那些实际使用和需要的依赖项发出元数据(尽管它们实际上不必在运行时存在,由于依赖项的延迟加载,任何不消耗缺少的依赖项的方法都可以仍然可以正常运行)。

在您的项目文件中添加更多 <PackageReference> 条目不会导致编译器输出发生任何变化,它只是允许您使用它们编写代码,并且该代码的存在会影响输出。

【讨论】:

  • 行不通。 NuGet 包。外用。如果我的另一个包可加载,这只是我的一个包测试。 AFAIK,异常是测试它的唯一方法。生成的可执行文件未更改并不完全正确。如果使用“生成单个文件”选项发布程序,则会发生很大变化。有一个嵌入了其他包的可执行文件。我可以加载它。这可能是唯一的方法。我只是尝试,当它失败时我继续前进。
【解决方案2】:

我当前的解决方案,在 Windows 和 Linux 上进行了测试,使用单个文件构建。

/// <summary>
/// Resolves available API members.
/// </summary>
internal static class ApiResolver {

    /// <summary>
    /// Gets an instance of the specified type.
    /// If it's not already loaded, the target assembly will be loaded.
    /// </summary>
    /// <typeparam name="TInterface">Type interface.</typeparam>
    /// <param name="assemblyName">Assembly name.</param>
    /// <param name="typeName">Type name.</param>
    /// <returns>Instance or null if not found.</returns>
    public static TInterface? GetInstance<TInterface>(string assemblyName, string typeName) where TInterface : class {
        Type? type;
        try {
            var assembly = Assembly.Load(assemblyName);
            type = assembly.GetType(typeName);
        }
        catch { return null; }
        return type is null ? null : (TInterface?)Activator.CreateInstance(type);
    }

}

如果无法获取实例,则返回 null。 所以它可以像这样很好地使用:

var myTarget = ApiResolver.GetInstance<IMyTargetApi>("OtherModule", "TheClass");
myTarget?.DoSomeStuff(withSomeData);

当然,要求目标类必须实现已知接口。但是我的包裹满足了这个要求。如果达不到,还是可以通过Reflection解决,但是会比较麻烦。

【讨论】:

    猜你喜欢
    • 2019-11-01
    • 2016-02-22
    • 2010-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-16
    • 1970-01-01
    相关资源
    最近更新 更多