You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Assembly.Load与Assembly.LoadFrom转换报错:对象必须实现IConvertible

问题分析与解决方案

嘿,我来帮你理清这个问题——我之前做插件热重载的时候也踩过这个坑,咱们一步步拆解:

为什么会出现“对象必须实现IConvertible”错误?

这个错误其实是个误导性的表象,真正的问题不是你的对象没实现IConvertible,而是CLR认为你要转换的两个类型是完全不同的类型,哪怕它们的类名、继承关系看起来一模一样。

当你用Assembly.Load(File.ReadAllBytes("myDerivedClass.dll"))加载插件时,这个程序集会被加载到无上下文(匿名加载上下文)中。而你的myBaseClassObject的基类类型,大概率是来自主应用程序集或者另一个被加载到默认上下文的程序集。这时候,哪怕插件里的派生类继承自同一个基类,CLR会因为加载上下文不同,把它们标记为不同的类型标识——简单说,就是CLR不认它们是“一家人”,自然无法通过Convert.ChangeType完成转换,只能抛出一个看似相关的错误。

Assembly.Load vs Assembly.LoadFrom的核心差异

这两个方法的本质区别在于程序集的加载上下文:

  • Assembly.LoadFrom("path"):会把程序集加载到默认加载上下文,CLR会跟踪程序集的物理路径,并且如果同一个路径的程序集被多次加载(版本、强命名一致),会复用已加载的实例。这时候插件里的派生类和主应用的基类,因为共享同一个基类程序集的实例,类型标识是一致的,所以转换不会出问题。
  • Assembly.Load(byte[]):加载的程序集属于无上下文,CLR不会关联它的物理路径,也不会复用实例。如果插件引用了基类程序集,CLR会在插件的加载上下文中重新加载一份基类程序集的副本——这就导致插件里的派生类继承的是“副本基类”,和主应用里的基类不是同一个类型,自然无法转换。

你哪里操作错了?

  1. 用错了类型转换方法:Convert.ChangeType本来就不是用来处理继承关系的类型转换的,它主要用于实现了IConvertible的类型之间的转换(比如int转string,DateTime转long等)。继承关系的转换应该用as关键字或者强制类型转换。
  2. 忽略了加载上下文对类型标识的影响:你默认认为Load(byte[])加载的插件类型和主应用的基类是兼容的,但实际上因为加载上下文隔离,CLR把它们当成了不同类型。

正确的解决办法

1. 确保基类程序集是强命名的

把基类所在的程序集标记为强命名(签名),这样不管从哪个加载上下文加载,CLR只要看到强命名、版本一致,就会认为是同一个类型。这是跨加载上下文类型兼容的基础。

2. 替换Convert.ChangeType为正确的类型转换

不要用Convert.ChangeType,直接用as或者强制转换:

// 安全转换,失败返回null
YourBaseClass classObj = myBaseClassObject as YourBaseClass;
if (classObj != null)
{
    // 执行你的逻辑
}

// 或者确定类型匹配时用强制转换
YourBaseClass classObj = (YourBaseClass)myBaseClassObject;

如果需要动态类型,也可以先转成基类再转dynamic:

dynamic classObj = (YourBaseClass)myBaseClassObject;

3. (可选)用应用域实现真正的插件卸载

如果你需要彻底卸载插件(而不只是重新加载),Assembly.Load(byte[])本身无法卸载单个程序集,只能通过创建单独的应用域来加载插件,卸载应用域时连带卸载插件程序集。不过这会涉及跨应用域通信,需要让基类继承MarshalByRefObject。


内容的提问来源于stack exchange,提问作者john_smith_lon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:02:54