使用NLua从Lua调用C#泛型方法时重复执行报错问题
解决NLua中重复调用泛型
ConvertToLuaTable的InvalidOperationException问题 问题根源
错误 InvalidOperationException: Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true 本质是:重复调用时,NLua没有正确获取到已实例化的泛型方法,反而拿到了未指定类型参数的泛型方法定义,导致无法执行晚绑定操作。首次调用时NLua会自动实例化泛型方法,但后续调用可能因内部缓存逻辑异常,复用了未闭合的泛型定义。
解决方案
方案1:将泛型方法改为非泛型版本
这是最直接的解决方式,绕过NLua的泛型方法绑定问题。
修改前的泛型方法示例:
public static LuaTable ConvertToLuaTable<T>(Lua lua, IEnumerable<T> collection) { var table = lua.NewTable(); int idx = 1; foreach (var item in collection) { table[idx++] = lua.AutoValue(item); } return table; }
修改后的非泛型方法:
public static LuaTable ConvertToLuaTable(Lua lua, IEnumerable collection) { var table = lua.NewTable(); int idx = 1; foreach (var item in collection) { // 利用NLua的AutoValue自动处理类型转换 table[idx++] = lua.AutoValue(item); } return table; }
Lua端调用时无需指定泛型参数,直接传入集合即可,彻底避免泛型绑定的问题。
方案2:缓存已实例化的泛型方法
如果不想修改方法的泛型结构,可以在C#端手动缓存每个类型对应的泛型方法实例,确保每次调用都是已闭合的泛型方法。
// 缓存不同类型对应的转换方法委托 private static readonly Dictionary<Type, Delegate> _converterCache = new(); // 原泛型方法保持不变 public static LuaTable ConvertToLuaTable<T>(Lua lua, IEnumerable<T> collection) { var table = lua.NewTable(); int idx = 1; foreach (var item in collection) { table[idx++] = lua.AutoValue(item); } return table; } // 暴露给Lua的非泛型入口方法 public static LuaTable ConvertToLuaTableCached(Lua lua, IEnumerable collection) { Type elementType = collection.GetType().GetGenericArguments()[0]; if (!_converterCache.TryGetValue(elementType, out var converter)) { // 实例化对应类型的泛型方法 var method = typeof(LsLua).GetMethod(nameof(ConvertToLuaTable), BindingFlags.Public | BindingFlags.Static); var genericMethod = method.MakeGenericMethod(elementType); // 创建委托并缓存 converter = Delegate.CreateDelegate( typeof(Func<Lua, IEnumerable, LuaTable>), genericMethod); _converterCache[elementType] = converter; } return ((Func<Lua, IEnumerable, LuaTable>)converter)(lua, collection); }
Lua端调用ConvertToLuaTableCached即可,内部会自动复用缓存的泛型方法实例,避免重复解析导致的错误。
方案3:提前注册已实例化的泛型方法
在Lua状态初始化阶段,提前为需要用到的类型实例化泛型方法并注册到Lua,而非直接注册泛型方法定义。
var lua = new Lua(); // 提前为常用类型实例化泛型方法并注册 var intConverter = typeof(LsLua).GetMethod(nameof(ConvertToLuaTable)) .MakeGenericMethod(typeof(int)); lua.RegisterFunction("ConvertToLuaTable_Int", null, intConverter); var stringConverter = typeof(LsLua).GetMethod(nameof(ConvertToLuaTable)) .MakeGenericMethod(typeof(string)); lua.RegisterFunction("ConvertToLuaTable_String", null, stringConverter);
Lua端根据集合类型调用对应的方法(如ConvertToLuaTable_Int处理整数集合),确保每次调用的都是已闭合的泛型方法,不会触发ContainsGenericParameters错误。
内容的提问来源于stack exchange,提问作者Ls16




