• xlua源码分析(三)C#访问lua的映射


    xlua源码分析(三)C#访问lua的映射

    上一节我们主要分析了lua call C#的无wrap实现。同时我们在第一节里提到过,C#使用LuaTable类持有lua层的table,以及使用Action委托持有lua层的function。而在xlua的官方文档中,推荐使用interface和delegate访问lua层数据结构:

    映射到一个interface

    这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。

    映射到delegate

    这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。

    delegate要怎样声明呢? 对于function的每个参数就声明一个输入类型的参数。 多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。

    参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。

    delegate的使用就更简单了,直接像个函数那样用就可以了。

    那么这一节我们就对照着Examples 04_LuaObjectOrented,来看一下如何把包含任意数据的lua table和包含任意参数的lua function映射到C#,让C#可以直接访问。

    首先看一下例子中用到的lua代码:

    local calc_mt = {
        __index = {
            Add = function(self, a, b)
                return (a + b) * self.Mult
            end,
            
            get_Item = function(self, index)
                return self.list[index + 1]
            end,
    
            set_Item = function(self, index, value)
                self.list[index + 1] = value
                self:notify({name = index, value = value})
            end,
            
            add_PropertyChanged = function(self, delegate)
                if self.notifylist == nil then
                    self.notifylist = {}
                end
                table.insert(self.notifylist, delegate)
                print('add',delegate)
            end,
                                    
            remove_PropertyChanged = function(self, delegate)
                for i=1, #self.notifylist do
                    if CS.System.Object.Equals(self.notifylist[i], delegate) then
                        table.remove(self.notifylist, i)
                        break
                    end
                end
                print('remove', delegate)
            end,
    
            notify = function(self, evt)
                if self.notifylist ~= nil then
                    for i=1, #self.notifylist do
                        self.notifylist[i](self, evt)
                    end
                end	
            end,
        }
    }
    
    Calc = {
        New = function (mult, ...)
            print(...)
            return setmetatable({Mult = mult, list = {'aaaa','bbbb','cccc'}}, calc_mt)
        end
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    这个例子很简单,就是定义了一个Calc.New的函数,这个函数会使用传入的参数构建一个新的table,并设置calc_mt作为它的metatable。calc_mt的__index表中定义了若干供C#访问的函数,如Addget_Itemset_Itemadd_PropertyChangedremove_PropertyChanged

    回到C#,C#层如果想要访问lua层的Calc.New,就需要定义一个和该函数匹配的委托。这个委托定义如下:

    [CSharpCallLua]
    public delegate ICalc CalcNew(int mult, params string[] args);
    
    • 1
    • 2

    委托有一个int类型的参数mult和不定数量的string类型参数args,int和string类型都可以很容易地从C#类型转换到对应的lua类型。再看返回值,这里的返回类型是一个ICalc的interface,它其实映射就是lua层的table,也就是Calc.New所返回的那个table。为了让xlua识别CalcNew这个委托类型是用来映射lua函数的,也就是要使用这个委托调用lua层函数,需要给CalcNew类型打上CSharpCallLua的标签,这样xlua就会生成代码来完成这一工作。

    映射lua table的ICalc定义如下:

    [CSharpCallLua]
    public interface ICalc
    {
        event EventHandler PropertyChanged;
    
        int Add(int a, int b);
        int Mult { get; set; }
    
        object this[int index] { get; set; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    接口类中包含了一个PropertyChanged的event,一个Add方法,一个Multi属性,还实现了下标操作符。那么想必大家都能猜出来,这里就是分别对应了lua层calc_mt的__index表中定义的若干函数。同样地,我们也需要为这个interface打上[CSharpCallLua]标签,这样xlua就会生成一个具体实现该接口的类。

    在理解映射思路之后,我们再看下测试代码:

    void Test(LuaEnv luaenv)
    {
        luaenv.DoString(script);
        CalcNew calc_new = luaenv.Global.GetInPath("Calc.New");
        ICalc calc = calc_new(10, "hi", "john"); //constructor
        Debug.Log("sum(*10) =" + calc.Add(1, 2));
        calc.Mult = 100;
        Debug.Log("sum(*100)=" + calc.Add(1, 2));
    
        Debug.Log("list[0]=" + calc[0]);
        Debug.Log("list[1]=" + calc[1]);
    
        calc.PropertyChanged += Notify;
        calc[1] = "dddd";
        Debug.Log("list[1]=" + calc[1]);
    
        calc.PropertyChanged -= Notify;
    
        calc[1] = "eeee";
        Debug.Log("list[1]=" + calc[1]);
    }
    
    void Notify(object sender, PropertyChangedEventArgs e)
    {
        Debug.Log(string.Format("{0} has property changed {1}={2}", sender, e.name, e.value));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    运行之后输出结果如下:

    xlua源码分析(三)1

    可以看到,我们通过映射的方式,访问到了lua的函数和table,而且很重要的一点是,测试代码中C#和lua实现了解耦,这种做法也是xlua的官方文档中所推荐的:

    使用建议

    1. 访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。
    2. 如果lua侧的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。

    那么现在,我们开始,跟着测试代码,一步步地研究背后的实现吧。

    第一步,就是调用了GetInPath,通过变量的名称获取到lua函数,再将其转换为CalcNew委托类型:

    public T GetInPath(string path)
    {
    #if THREAD_SAFE || HOTFIX_ENABLE
        lock (luaEnv.luaEnvLock)
        {
    #endif
            var L = luaEnv.L;
            var translator = luaEnv.translator;
            int oldTop = LuaAPI.lua_gettop(L);
            LuaAPI.lua_getref(L, luaReference);
            if (0 != LuaAPI.xlua_pgettable_bypath(L, -1, path))
            {
                luaEnv.ThrowExceptionFromError(oldTop);
            }
            LuaTypes lua_type = LuaAPI.lua_type(L, -1);
            if (lua_type == LuaTypes.LUA_TNIL && typeof(T).IsValueType())
            {
                throw new InvalidCastException("can not assign nil to " + typeof(T).GetFriendlyName());
            }
    
            T value;
            try
            {
                translator.Get(L, -1, out value);
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                LuaAPI.lua_settop(L, oldTop);
            }
            return value;
    #if THREAD_SAFE || HOTFIX_ENABLE
        }
    #endif
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    重点需要关注的其实就是这句translator.Get(L, -1, out value);,它负责对lua栈上的函数进行类型转换。这个委托类型并不是实现注册好的类型,那么就会走到通用的GetObject函数:

    public void Get(RealStatePtr L, int index, out T v)
    {
        Func get_func;
        if (tryGetGetFuncByType(typeof(T), out get_func))
        {
            v = get_func(L, index);
        }
        else
        {
            v = (T)GetObject(L, index, typeof(T));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这个GetObject函数我们在前面的章节中也分析过,对于不是userdata的lua对象,它会寻找一个caster函数进行转换,如果找不到,则会通过一系列规则生成一个caster:

    public ObjectCast GetCaster(Type type)
    {
        if (type.IsByRef) type = type.GetElementType();
    
        Type underlyingType = Nullable.GetUnderlyingType(type);
        if (underlyingType != null)
        {
            return genNullableCaster(GetCaster(underlyingType)); 
        }
        ObjectCast oc;
        if (!castersMap.TryGetValue(type, out oc))
        {
            oc = genCaster(type);
            castersMap.Add(type, oc);
        }
        return oc;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这里的委托类型是我们自定义的,默认的castersMap中显然不包含,那么xlua就会为我们生成一个:

    ObjectCast fixTypeGetter = (RealStatePtr L, int idx, object target) =>
    {
        if (LuaAPI.lua_type(L, idx) == LuaTypes.LUA_TUSERDATA)
        {
            object obj = translator.SafeGetCSObj(L, idx);
            return (obj != null && type.IsAssignableFrom(obj.GetType())) ? obj : null;
        }
        return null;
    }; 
    
    if (typeof(Delegate).IsAssignableFrom(type))
    {
        return (RealStatePtr L, int idx, object target) =>
        {
            object obj = fixTypeGetter(L, idx, target);
            if (obj != null) return obj;
    
            if (!LuaAPI.lua_isfunction(L, idx))
            {
                return null;
            }
    
            return translator.CreateDelegateBridge(L, type, idx);
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这里的关键也是在translator.CreateDelegateBridge这句,这个函数之前我们也分析过,它负责生成一个DelegateBridge对象。这个对象就是指代lua函数用的,它自身可以与多个C#的委托绑定。

    bridge = new DelegateBridge(reference, luaEnv);
    try {
        var ret = getDelegate(bridge, delegateType);
        bridge.AddDelegate(delegateType, ret);
        delegate_bridges[reference] = new WeakReference(bridge);
        return ret;
    }
    catch(Exception e)
    {
        bridge.Dispose();
        throw e;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    getDelegate这个函数,会根据传入的delegateType,调用DelegateBridgeBase.GetDelegateByType生成对应类型的Delegate对象,它是个virtual方法,我们在生成代码之后,就会产生继承自它的DelegateBridge.GetDelegateByTypeoverride方法,这段生成代码位于DelegatesGenBridge.cs这个文件里:

    public partial class DelegateBridge : DelegateBridgeBase
    {
        public override Delegate GetDelegateByType(Type type)
        {
            if (type == typeof(System.Action))
            {
                return new System.Action(__Gen_Delegate_Imp0);
            }
    
            if (type == typeof(UnityEngine.Events.UnityAction))
            {
                return new UnityEngine.Events.UnityAction(__Gen_Delegate_Imp0);
            }
    
            if (type == typeof(System.Func))
            {
                return new System.Func(__Gen_Delegate_Imp1);
            }
    
            if (type == typeof(System.Action))
            {
                return new System.Action(__Gen_Delegate_Imp2);
            }
    
            if (type == typeof(System.Action))
            {
                return new System.Action(__Gen_Delegate_Imp3);
            }
    
            if (type == typeof(XLuaTest.IntParam))
            {
                return new XLuaTest.IntParam(__Gen_Delegate_Imp4);
            }
    
            if (type == typeof(XLuaTest.Vector3Param))
            {
                return new XLuaTest.Vector3Param(__Gen_Delegate_Imp5);
            }
    
            if (type == typeof(XLuaTest.CustomValueTypeParam))
            {
                return new XLuaTest.CustomValueTypeParam(__Gen_Delegate_Imp6);
            }
    
            if (type == typeof(XLuaTest.EnumParam))
            {
                return new XLuaTest.EnumParam(__Gen_Delegate_Imp7);
            }
    
            if (type == typeof(XLuaTest.DecimalParam))
            {
                return new XLuaTest.DecimalParam(__Gen_Delegate_Imp8);
            }
    
            if (type == typeof(XLuaTest.ArrayAccess))
            {
                return new XLuaTest.ArrayAccess(__Gen_Delegate_Imp9);
            }
    
            if (type == typeof(System.Action))
            {
                return new System.Action(__Gen_Delegate_Imp10);
            }
    
            if (type == typeof(Tutorial.CSCallLua.FDelegate))
            {
                return new Tutorial.CSCallLua.FDelegate(__Gen_Delegate_Imp11);
            }
    
            if (type == typeof(Tutorial.CSCallLua.GetE))
            {
                return new Tutorial.CSCallLua.GetE(__Gen_Delegate_Imp12);
            }
    
            if (type == typeof(XLuaTest.InvokeLua.CalcNew))
            {
                return new XLuaTest.InvokeLua.CalcNew(__Gen_Delegate_Imp13);
            }
    
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    得到Delegate之后,这里会将其进行缓存,这样下次遇到相同类型直接取出该委托即可。DelegateBridgeBase类缓存Delegate的数据结构比较有意思,它有一对firstKey和firstValue,然后一个Dictionary的字典所组成,缓存时会优先将数据保存到firstKey和firstValue上,这样取出的时候就无需对字典进行查找,查找效率更高。

    public bool TryGetDelegate(Type key, out Delegate value)
    {
        if(key == firstKey)
        {
            value = firstValue;
            return true;
        }
        if (bindTo != null)
        {
            return bindTo.TryGetValue(key, out value);
        }
        value = null;
        return false;
    }
    
    public void AddDelegate(Type key, Delegate value)
    {
        if (key == firstKey)
        {
            throw new ArgumentException("An element with the same key already exists in the dictionary.");
        }
    
        if (firstKey == null && bindTo == null) // nothing 
        {
            firstKey = key;
            firstValue = value;
        }
        else if (firstKey != null && bindTo == null) // one key existed
        {
            bindTo = new Dictionary();
            bindTo.Add(firstKey, firstValue);
            firstKey = null;
            firstValue = null;
            bindTo.Add(key, value);
        }
        else
        {
            bindTo.Add(key, value);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    就这样,这个新生成的委托经过辗转终于返回到了测试代码,也就是calc_new对象,那么我们就可以直接通过委托的方式调用它,此时就会触发生成的__Gen_Delegate_Imp13函数了,我们来看看生成的代码长什么样:

    public XLuaTest.InvokeLua.ICalc __Gen_Delegate_Imp13(int p0, string[] p1)
    {
    #if THREAD_SAFE || HOTFIX_ENABLE
        lock (luaEnv.luaEnvLock)
        {
    #endif
            RealStatePtr L = luaEnv.rawL;
            int errFunc = LuaAPI.pcall_prepare(L, errorFuncRef, luaReference);
            ObjectTranslator translator = luaEnv.translator;
            LuaAPI.xlua_pushinteger(L, p0);
            if (p1 != null)  { for (int __gen_i = 0; __gen_i < p1.Length; ++__gen_i) LuaAPI.lua_pushstring(L, p1[__gen_i]); };
            
            PCall(L, 1 + (p1 == null ? 0 : p1.Length), 1, errFunc);
            
            
            XLuaTest.InvokeLua.ICalc __gen_ret = (XLuaTest.InvokeLua.ICalc)translator.GetObject(L, errFunc + 1, typeof(XLuaTest.InvokeLua.ICalc));
            LuaAPI.lua_settop(L, errFunc - 1);
            return  __gen_ret;
    #if THREAD_SAFE || HOTFIX_ENABLE
        }
    #endif
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    代码逻辑很简单,就是准备调用环境,然后把C#的参数push到lua层,然后pcall调用,然后从lua栈中取出返回的结果,由于lua是弱类型的,无法事先知道返回值的类型,所以这里只能使用通用的GetObject函数对lua的返回值进行类型转换。

    同样,ICalc类型是我们自定义的,默认的castersMap是不包含的,也需要生成一个caster:

    return (RealStatePtr L, int idx, object target) =>
    {
        object obj = fixTypeGetter(L, idx, target);
        if (obj != null) return obj;
    
        if (!LuaAPI.lua_istable(L, idx))
        {
            return null;
        }
        return translator.CreateInterfaceBridge(L, type, idx);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    那么,这里的关键就是在translator.CreateInterfaceBridge上了,与委托非常类似,这里会根据interface的类型,寻找负责生成interface对象的函数:

    public object CreateInterfaceBridge(RealStatePtr L, Type interfaceType, int idx)
    {
        Func creator;
    
        if (!interfaceBridgeCreators.TryGetValue(interfaceType, out creator))
        {
    #if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0
            var bridgeType = ce.EmitInterfaceImpl(interfaceType);
            creator = (int reference, LuaEnv luaenv) =>
            {
                return Activator.CreateInstance(bridgeType, new object[] { reference, luaEnv }) as LuaBase;
            };
            interfaceBridgeCreators.Add(interfaceType, creator);
    #else
            throw new InvalidCastException("This type must add to CSharpCallLua: " + interfaceType);
    #endif
        }
        LuaAPI.lua_pushvalue(L, idx);
        return creator(LuaAPI.luaL_ref(L), luaEnv);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    往interfaceBridgeCreators注册creator的逻辑就是在生成代码中完成的,位于XLuaGenAutoRegister.cs中:

    static void Init(LuaEnv luaenv, ObjectTranslator translator)
    {
        
        wrapInit0(luaenv, translator);
        
        
        translator.AddInterfaceBridgeCreator(typeof(System.Collections.IEnumerator), SystemCollectionsIEnumeratorBridge.__Create);
        
        translator.AddInterfaceBridgeCreator(typeof(XLuaTest.IExchanger), XLuaTestIExchangerBridge.__Create);
        
        translator.AddInterfaceBridgeCreator(typeof(Tutorial.CSCallLua.ItfD), TutorialCSCallLuaItfDBridge.__Create);
        
        translator.AddInterfaceBridgeCreator(typeof(XLuaTest.InvokeLua.ICalc), XLuaTestInvokeLuaICalcBridge.__Create);
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    XLuaTestInvokeLuaICalcBridge是继承自ICalc接口的类,它负责实现ICalc的功能,也就是我们一开始提到的一个PropertyChanged的event +=和-=操作,一个Add方法,一个Multi属性,以及下标操作符。__Create方法就是简单了返回了一个XLuaTestInvokeLuaICalcBridge对象:

    public class XLuaTestInvokeLuaICalcBridge : LuaBase, XLuaTest.InvokeLua.ICalc
    {
        public static LuaBase __Create(int reference, LuaEnv luaenv)
        {
            return new XLuaTestInvokeLuaICalcBridge(reference, luaenv);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    有了ICalc对象后,我们再次回到例子中,例子中接下来调用了Add方法与Multi的set属性,XLuaTestInvokeLuaICalcBridge类对它们的实现都比较简单,这里就不再赘述了。接下来是下标访问,对于get来说会去尝试访问lua层的get_item函数,而对于set来说则会去访问lua层的set_item函数。例子里还往PropertyChanged事件中注册了一个Notify方法,这时则会触发lua层的add_PropertyChanged函数,把C#的Notify方法push到lua层。

    上一节我们提到,把C#对象push到lua层时,会调用到xlua的getTypeId方法,用来获取表示对象类的唯一ID,对于Notify方法来说,它就是一个委托,而委托实质上使用的是同一个type id:

    if (typeof(MulticastDelegate).IsAssignableFrom(type))
    {
        if (common_delegate_meta == -1) throw new Exception("Fatal Exception! Delegate Metatable not inited!");
        TryDelayWrapLoader(L, type);
        return common_delegate_meta;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    TryDelayWrapLoader我们上一节分析过,这里就不展开了,由于没有wrap,还是通过反射生成类的各种table。最终lua层缓存了一个表示C# Notify方法的userdata。

    此时再对table进行set_item,就会触发Notify方法调用了,对于delegate来说,xlua在初始化时就往metatable里设置了__call元方法:

    public void CreateDelegateMetatable(RealStatePtr L)
    {
        Utils.BeginObjectRegister(null, L, this, 3, 0, 0, 0, common_delegate_meta);
        Utils.RegisterFunc(L, Utils.OBJ_META_IDX, "__call", StaticLuaCallbacks.DelegateCall);
        Utils.RegisterFunc(L, Utils.OBJ_META_IDX, "__add", StaticLuaCallbacks.DelegateCombine);
        Utils.RegisterFunc(L, Utils.OBJ_META_IDX, "__sub", StaticLuaCallbacks.DelegateRemove);
        Utils.EndObjectRegister(null, L, this, null, null,
                typeof(System.MulticastDelegate), null, null);
    }
    
    [MonoPInvokeCallback(typeof(LuaCSFunction))]
    public static int DelegateCall(RealStatePtr L)
    {
        try
        {
            ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
            object objDelegate = translator.FastGetCSObj(L, 1);
            if (objDelegate == null || !(objDelegate is Delegate))
            {
                return LuaAPI.luaL_error(L, "trying to invoke a value that is not delegate nor callable");
            }
            return translator.methodWrapsCache.GetDelegateWrap(objDelegate.GetType())(L);
        }
        catch (Exception e)
        {
            return LuaAPI.luaL_error(L, "c# exception in DelegateCall:" + e);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    GetDelegateWrap方法就是根据委托的类型,反射取出它的Inovke方法,然后包装到MethodWrap的Call方法中,进行最终的反射调用。

  • 相关阅读:
    排错-关于clion not found visual studio 的问题
    第2关:ACL访问控制列表
    Android案例手册 - 仅一个文件的展开收缩LinearLayout
    划重点!3DEXPERIENCE SOLIDWORKS 2024 十大增强功能
    在 Windows Server RDS 服务器 上重置 120 天宽限期
    英语口语学习(07-09)
    【系统架构设计师】一、计算机系统基础知识(指令系统|存储系统|输入输出技术|总线结构)
    ChatGPT解决hmm...something seems to have gone wrong.
    Node18.x基础使用总结(二)
    积分商城的三种运营方向
  • 原文地址:https://blog.csdn.net/weixin_45776473/article/details/134476335