IsAssignableFrom(甚至是 ImplementInterface)是如何工作的

how IsAssignableFrom (or even ImplementInterface) works

我有一个来自 Whosebug 的 c# 插件系统。 关键部分是Assembly.LoadFile、Type.IsAssignableFrom和Activator.CreateInstance。它有效,但是我不完全理解 IsAssignableFrom 如何识别从外部程序集加载的类型。 说,我有

public interface PluginInterface 
{
    int calculateSomething();
}

我编译成PluginInterface.dll。

PluginInterface.dll是示例插件中的引用,eg.:

public class CalculateOnePlugin : PluginInterface
{
    public int calculateSomething() { return 1; }
}

编译成CalculateOnePlugin.dll.

现在,参考 PluginInterface.dll 创建一个应用程序并尝试加载和使用 CalculateOnePlugin.dll:

var pluginAssembly = Assembly.LoadFile("CalculateOnePlugin.dll");
Type interfaceType = typeof(PluginInterface);
Type[] typesInLoadedAssembly = pluginAssembly.GetTypes();
Type pluginType = null;
foreach (var typeInLoadedAssembly in typesInLoadedAssembly)
    if (interfaceType.IsAssignableFrom(typeInLoadedAssembly)) // ???
    {
        pluginType = typeInLoadedAssembly;
        break;
    }
if (pluginType!=null) 
{
    PluginInterface pi = (PluginInterface)Activator.CreateInstance(pluginType);
    // and do what you want with that plugin object
}

我的问题是 IsAssignableFrom 如何识别和匹配从两个外部程序集加载的类型? IsAssignableFrom 考虑了哪些程序集或 class 属性?

如果我复制 PluginInterface 的源代码并将其编译成(例如)AnotherPluginInterface.dll 并在构建 CalculateOnePlugin.dll 时使用此引用,那么它将无法工作。所以类型匹配不仅仅关心方法签名,例如,它以某种方式知道程序集的来源和类型。

当我为应用程序和插件使用两个不同版本的PluginInterface时(比如说在后者中添加了一个方法)它不会再次起作用,所以这两个dll的来源是不够的,必须里面有一些版本的东西。

但简单地重新编译两种用法(应用程序和插件)之间的 PluginInterface 不会造成混乱,它有效。

PluginInterface.dll 的 GUID 是否包含在 CalculateOnePlugin.dll 的引用中,这就是 IsAssignableFrom 知道它们相同的方式吗?或者类型使用类似散列的值?还是两者兼而有之?

可以自己查看源码
IsAssignableFrom calls ImplementInterface(Type ifaceType) 检查类型是否实现接口。

现在,每个类型(class/interface/struct)都在程序集中定义。
每个程序集都有一个 Fully Qualified Name,因此即使您 copy-paste 源代码

我们也知道定义接口的位置(哪个程序集)

有一个复杂的程序集解析过程在幕后进行,其中引用的类型要么映射到已加载的程序集,要么导致动态加载必要的程序集。当您加载插件时,这可能会有点棘手,尤其是当您有多个位置存在引用程序集时。这可能会导致奇怪的情况,例如加载同一程序集的两个副本(一个来自您的主文件夹,另一个来自您的插件文件夹),然后由于加载所有这些类型的两个不同的不兼容副本而导致运行时错误。

实际匹配过程通常使用 George Vovos 的回答中提到的完全限定名称,尽管 binding redirects 之类的东西会使图片复杂化。

您可以通过多种方式使用 AppDomain.AssemblyResolve 事件自定义程序集解析过程。如果你正在创建一个插件系统,那么对程序集解析过程进行一些研究是非常值得的,因为有一些陷阱,特别是如果你做一些事情,比如重新加载插件的更新版本 'live'.

My question is how IsAssignableFrom identifies and matches the types loaded from two external assembly? What assembly or class properties are taken into consideration by IsAssignableFrom?

本质上是进行了等价比较。

A Type 派生自名为 RunTimeType 的 class。

RunTimeType 有一个 Equality 运算符,它执行如下检查:

 return RuntimeTypeHandle.IsEquivalentTo(this, otherRtType);

很遗憾,RuntimeTypeHandle.IsEquivalentTo 被标记为 CLR 核心的一部分,无法反映出来,所以我无法从代码中准确地告诉您它的外观或功能。

然而,根据 this article entitled "Anatomy of a .NET Assembly"

Type values are stored using a string representing the type name and fully qualified assembly name (for example, MyNs.MyType, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0123456789abcdef). If the type is in the current assembly or mscorlib then just the type name can be used.

因此,程序集名称、区域性、版本、命名空间和 public 密钥标记都将显示为匹配,以便唯一标识一个类型。