You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

使用NLua从Lua调用C#泛型方法时重复执行报错问题

解决NLua中重复调用泛型ConvertToLuaTableInvalidOperationException问题

问题根源

错误 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

火山引擎 最新活动