C# Unity动态类型转换报RuntimeBinderException同全名类型异常如何修复
问题背景
我在对动态生成的对象执行类型转换时触发异常,不清楚异常成因和修复方案,开发环境为Unity引擎下的C#。
我使用基于System.Reflection.Emit实现的TypeExtender库在运行时创建动态对象,通过TypeBuilder生成动态类型,创建对象的代码如下:
private object createRunTimeCurrencyTable(Type type) { var typeExtender = new TypeExtender("CurrencyEntity", typeof(CurrencyEntityChild)); typeExtender.AddAttribute<EntityAttribute>(new object[] { $"{type.Name}" }); typeExtender.AddProperty("ID", typeof(int), typeof(ColumnAttribute), new object[] { "ID", ColumnAttribute.DataType.DOUBLE, true }); typeExtender.AddProperty("amount", typeof(double), typeof(ColumnAttribute), new object[] { "Amount" , ColumnAttribute.DataType.DOUBLE}); typeExtender.AddProperty<Currency>("currency"); var returnedClass = typeExtender.FetchType(); return Activator.CreateInstance(returnedClass); }
根据库的说明,生成的动态类型会从传入的基类派生,本次代码传入的基类为CurrencyEntityChild,后续我通过如下泛型反射方法尝试将返回的动态对象转换为CurrencyEntity类型:
dynamic DynamicCast(object entity, Type to) { var openCast = this.GetType().GetMethod("Cast", BindingFlags.Static | BindingFlags.NonPublic); var closeCast = openCast.MakeGenericMethod(to); return closeCast.Invoke(entity, new[] { entity }); } static T Cast<T>(object entity) where T : class { return entity as T; }
异常表现
上述转换操作返回null对象,访问该null对象属性时触发如下异常:
RuntimeBinderException: More than one type in the binding has the same full name
异常成因
as转换返回null已经说明动态生成的类型和目标转换类型不匹配,最终抛出RuntimeBinderException是三个问题叠加导致:
- 类型继承关系不匹配:你给TypeExtender传入的基类是
CurrencyEntityChild,但转换目标是CurrencyEntity,如果CurrencyEntityChild没有继承自CurrencyEntity,动态生成的子类自然无法向上转型为CurrencyEntity,as操作会直接返回null。 - 重复生成同名动态类型:每次调用
createRunTimeCurrencyTable时,TypeExtender都会重新创建一个全限定名为CurrencyEntity的新动态类型,这些类型被生成在独立的匿名动态程序集中,虽然全名相同但分属不同的类型加载上下文。Unity的运行时绑定器解析dynamic类型时会扫描所有已加载类型,发现多个同名类型就会抛出该异常,重复调用创建方法时问题必然复现。 - 反射调用参数错误:
Cast是静态方法,反射调用时第一个参数应该传null,你把待转换的entity作为实例参数传入,本身就会导致调用行为异常。
修复方案
按优先级依次调整即可:
- 对齐基类逻辑:如果需要将动态类型转换为
CurrencyEntity,直接给TypeExtender传入typeof(CurrencyEntity)作为基类;如果确实要基于CurrencyEntityChild派生,就把转换目标类型改为CurrencyEntityChild,前提是确认两者的继承关系成立。 - 缓存动态生成的类型:不要每次调用创建方法都重新生成同名动态类型,将生成完成的
returnedClass存在静态缓存中,同结构类型仅生成一次,从根源避免多同名类型冲突。 - 修正反射调用参数:将
closeCast.Invoke(entity, new[] { entity })改为closeCast.Invoke(null, new[] { entity }),符合静态方法反射调用的参数规则。 - 可选优化:去掉不必要的dynamic返回,转换完成后直接用基类类型接收对象,不要通过dynamic访问属性,彻底绕过RuntimeBinder的解析逻辑。
修正后的参考代码:
// 动态类型缓存,避免重复生成同名类型 private static Dictionary<string, Type> _runtimeTypeCache = new Dictionary<string, Type>(); private object createRunTimeCurrencyTable(Type type) { string typeKey = $"CurrencyEntity_{type.Name}"; Type returnedClass; if (!_runtimeTypeCache.TryGetValue(typeKey, out returnedClass)) { // 传入正确的基类保证类型转换兼容 var typeExtender = new TypeExtender(typeKey, typeof(CurrencyEntity)); typeExtender.AddAttribute<EntityAttribute>(new object[] { $"{type.Name}" }); typeExtender.AddProperty("ID", typeof(int), typeof(ColumnAttribute), new object[] { "ID", ColumnAttribute.DataType.DOUBLE, true }); typeExtender.AddProperty("amount", typeof(double), typeof(ColumnAttribute), new object[] { "Amount" , ColumnAttribute.DataType.DOUBLE}); typeExtender.AddProperty<Currency>("currency"); returnedClass = typeExtender.FetchType(); _runtimeTypeCache.Add(typeKey, returnedClass); } return Activator.CreateInstance(returnedClass); } // 修正静态方法反射调用逻辑 object DynamicCast(object entity, Type to) { var openCast = this.GetType().GetMethod("Cast", BindingFlags.Static | BindingFlags.NonPublic); var closeCast = openCast.MakeGenericMethod(to); // 静态方法Invoke第一个参数传null return closeCast.Invoke(null, new[] { entity }); } static T Cast<T>(object entity) where T : class { return entity as T; }
内容的提问来源于stack exchange,提问作者Odai A. Ali
相关产品推荐
相关产品推荐

