动态加载类型时强制转换抛出InvalidCastException异常的问题求助
问题分析与解决
核心原因
你遇到的问题是同一个物理DLL被CLR加载了两次,生成了两个独立的程序集实例。即使类型的名称、版本、签名完全一致,CLR会认为它们是不同的类型——因为类型的唯一性由其所属的程序集实例+类型全名共同决定。
你的代码里用Assembly.LoadFile加载了当前执行程序集的物理文件,而当前程序集已经被CLR加载过一次了,这就导致了两个相同但独立的程序集实例,所以:
componentType == typeof(Component)输出False- 强制转换时抛出
InvalidCastException,因为两个Component类型分属不同程序集实例 - DI单例失效,因为容器会把这两个不同类型当成不同的服务,分别创建实例
解决方案
1. 避免重复加载同一程序集
如果是加载当前已运行的程序集,直接用Assembly.GetExecutingAssembly()即可,不需要再调用LoadFile:
class Program { static void Main() { Assembly assembly = Assembly.GetExecutingAssembly(); var componentType = assembly.GetExportedTypes().FirstOrDefault(x => x.Name == "Component"); Console.WriteLine(componentType == typeof(Component)); // 现在会输出True ConstructorInfo constructor = componentType.GetConstructors()[0]; var component = constructor.Invoke(new object[0]); var k = (Component)component; // 强制转换成功 } }
2. 跨动态加载DLL的DI场景处理
如果是加载外部DLL(而非当前程序集),需要确保共享类型只被加载一次:
- 抽离共享契约:把需要跨程序集共享的类型(比如DI服务的接口、实体类)放到单独的类库项目(如
MyApp.Contracts),主程序和所有动态加载的DLL都引用这个类库。 - 使用正确的加载方法:
- 优先用
Assembly.Load加载程序集(通过程序集全名),CLR会自动检查缓存,避免重复加载相同标识的程序集。 - 如果必须用路径加载,使用
Assembly.LoadFrom,它也会检查当前AppDomain中是否已加载相同路径的程序集,避免重复加载。
- 优先用
- 预检查已加载的程序集:加载前先遍历
AppDomain.CurrentDomain.GetAssemblies(),如果目标程序集已存在,直接复用,不重复加载:
string dllPath = @"C:\Path\To\Your\External.dll"; var existingAssembly = AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(a => a.Location.Equals(dllPath, StringComparison.OrdinalIgnoreCase)); Assembly assembly = existingAssembly ?? Assembly.LoadFrom(dllPath);
3. DI单例修复
确保DI容器注册和解析时,使用的是同一个程序集实例中的类型:
- 注册服务时,用动态加载得到的类型或者共享契约类型
- 解析时,同样使用一致的类型标识,避免因为程序集重复加载导致类型不匹配
内容的提问来源于stack exchange,提问作者markt
相关产品推荐
相关产品推荐

