C#用CSharpScript创建动态对象时出现InvalidCastException异常
异常触发原因
- C# 匿名类型的类型判定规则:仅在同一程序集内,按相同顺序定义、属性名称/类型完全一致的匿名类型,会被编译器自动复用同一个类定义;跨程序集边界的匿名类型,即使结构100%匹配,CLR也会视为完全独立、无兼容关系的不同类型。
CSharpScript执行动态代码时,会在内存中生成独立的临时编译程序集。脚本代码中定义的new { FilmSourceId = 2, Source = "Something" },是在这个临时程序集内生成的匿名类型(对应异常中带#1后缀的<>f__AnonymousType0#12[System.Int32,System.String]类型);而Main方法lambda表达式中的匿名类型,是在主业务程序集内编译生成的(对应异常中无#1后缀的<>f__AnonymousType02[System.Int32,System.String]类型)。- 两个分属不同程序集的匿名类型不存在类型兼容关系,直接执行
(TResult)强转就会触发当前的转换异常。 - 非动态创建场景运行正常的原因:该场景下创建匿名类型、执行强转的代码都在同一个主程序集内,编译器自动复用了同一个匿名类型定义,不存在跨程序集的类型边界问题。
修复方案
- 最优方案:用显式具名类型替代跨边界传递的匿名类型
不在跨程序集、跨动态编译边界的逻辑中使用匿名类型。定义一个包含FilmSourceId、Source属性的普通类或record类型,无论是CSharpScript动态代码创建对象,还是主程序集的泛型方法做类型转换,都直接引用这个具名类型。该方案从根源上规避了匿名类型的跨程序集不兼容问题,没有长期维护隐患。 - 兼容动态逻辑的方案:用结构映射替代直接强转
不尝试把CSharpScript返回的动态对象直接强转为主程序集内的匿名类型。通过反射读取动态对象的同名称性值,构造对应的目标类型实例完成属性赋值即可。该方式不依赖跨程序集的匿名类型复用,只要属性名称、类型匹配就能正常工作。 - 需要规避的错误思路
不要试图让CSharpScript编译出和主程序集完全一致的匿名类型。匿名类型是编译器生成的internal访问级别的类型,跨程序集无法获取可引用的类型句柄,即使结构完全匹配,也会生成独立的新类型,该路径本质不具备可行性。
内容的提问来源于stack exchange,提问作者DVL
相关产品推荐
相关产品推荐

