Xlua中lua语言与C#之间的调用

Xlua学习笔记整理。还有很多没整理完,慢慢来。


文章目录

    • 一.Xlua中Lua文件加载的两种方式
    • 二.CsharpCallLua
        • ---- 1.全局字段
        • ---- 2.全局table
            • -------- (1).table为数组时
            • -------- (2).table为字典时(键值对)
            • -------- (3).table为类时(Class)
            • ----------------- 1)映射到class,值拷贝
            • ----------------- 2)映射到interface,引用拷贝(推荐方式)
            • ----------------- 3)映射到xlua自身的LuaTable.
        • ---- 3.全局方法函数function
            • ----------------- 1)映射到委托delegate(推荐)
            • ----------------- 2)映射到LuaFunction
    • 三.LuaCallCsharp
        • ---- 1.实例化
        • ---- 2.访问类中的字段属性方法
        • ---- 3.调用方法
            • -------- (1).调用父类属性与方法
            • -------- (2).调用重载方法
            • -------- (3).调用带参方法
            • ----------------- 1)调用可变参数方法
            • ----------------- 2)调用参数有结构体的方法
            • ----------------- 3)调用参数有接口的方法
            • ----------------- 4)调用参数有委托的方法
            • -------- (4).调用具有多返回值方法
            • -------- (5).调用泛型方法
        • ---- 4.调用委托事件
    • 三.其他注意事项

一.Xlua中Lua文件加载的两种方式

1.直接执行字符串:

LuaEnv  env = new LuaEnv();
env.DoString("print('这是我的第一个lua程序'");

2.加载Lua文件

使用Require函数加载Lua文件

require就是一个个的调用Loader,查找出匹配的lua文件,然后执行该文件。

(1)使用默认Loader加载

 LuaEnv env = new LuaEnv();
 env.DoString("require 'SimpleLua' ");//不用加lua后缀

(2)使用自定义Loader加载

通过Addloader可以注册个回调,该回调参数是字符串,Lua代码里头调用require时,参数将会自动传给回调,
回调中就可以根据这个参数去加载指定文件,如果需要支持调试,需要把filepath修改为真实路径传出。

        LuaEnv env = new LuaEnv();
        env.AddLoader(CustomMyLoader);
        env.DoString("require 'HelloWord'");
 
        /// 
        /// 定义回调方法
        /// 功能:
        ///   本方法主要功能是自定义lua文件路径
        /// 
        /// 文件名称
        /// 
        private byte[] CustomMyLoader(ref string fileName)
        {
            byte[] byArrayReturn = null;//返回数据
            //定义lua路径
            string luaPath = Application.dataPath + "/StreamingAssets/" + fileName + ".lua";
            //读取lua路径中指定lua文件内容
            string strLuaContent =File.ReadAllText(luaPath);
            //数据类型转换
            byArrayReturn = System.Text.Encoding.UTF8.GetBytes(strLuaContent);
            return byArrayReturn;
        }
 

注意:
1.使用默认Loader加载lua文件,必须放在Resources特殊目录下,否则查找不到无法加载。

2.因为Resource只支持有限的后缀,放Resource下的lua文件需要加上txt后缀。

3.自定义Loader可以进一步扩展加载路径,可以按照自己的方式进行加载lua与执行lua代码。

4.require本质是按照既定的查找顺序,找到需要的lua程序,否则返回nil,然后报错。

二.CsharpCallLua

主要是C#调用Lua的全局数据类型,包括字段,table,函数。

最大特点: env.Global.Get调用lua中的表,字段,函数。

---- 1.全局字段

访问LuaEnv.Global就可以了,上面有个模版Get方法,可指定返回的类型。
eg:

Lua

str="全局变量"
number=10

C#

     string str1 = env.Global.Get("str");//字符串类型
     int number = env.Global.Get("number");//数字类型

---- 2.全局table

由于Lua中Table可以充当很多职能,如数组,字典(键值对),类(Class)。根据其职能不同调用也不同。

-------- (1).table为数组时

直接映射到List

eg:

Lua

progamLanguage={"C#","Lua","C++","C"}

C#

 List listGameLan = env.Global.Get>("progamLanguage");

-------- (2).table为字典时(键值对)

直接映射到Dictionary

eg:

Lua

gameLanguage={str1="C#语言",str2="lua语言",str3="C++语言",str4="C语言"}

C#

Dictionary dicGameLan = env.Global.Get>("gameLanguage");

-------- (3).table为类时(Class)

当Lua中的table为类时有三种调用方法,一种是映射到普通class或struct,为值拷贝。一种是映射到接口interface,是引用拷贝。一种是映射到Xlua自身的LuaTable上。

Lua

--定义一个综合表(lua中的oop思想)
GameUser=
{
	name="小河",
	age=199,
	ID="188875221",

	Speak=function()
	   print("lua玩家在讨论中")
	end,
	Walking=function()
	print("lua玩家在健身中")
	end,
	Calulation=function(this,num1,num2) --说明:this 这里命名可以任意,表示当前对象(即GameUser)
	   print("lua玩家加年龄和")
	  return this.age+num1+num2
    end
}
----------------- 1)映射到class,值拷贝

定义一个class,有对应table的字段的public属性,而且有无参数构造函数即可。table的属性可以多于或者少于class的属性,可以嵌套其他复杂类型。

eg:

C#

public class GameUser
{
    public string name;
    public int age;
    public int ID;
    public void Speak()
    {

    }
    public void Walking()
    {

    }
    public int Calulation(int num1, int num2)
    {
        return 0;
    }
}
 public class CsharpCallLuaTableByClass : MonoBehaviour
 {
       //lua环境(官方建议全局唯一)
        LuaEnv env = null;
        private void Start()
        {
            env = new LuaEnv();
            //不用加lua后缀
            env.DoString("require 'CsharpCallLuaTable' ");
            //**********************************************************************************
            //方式1: 使用class(struct)来映射得到lua中的table内容。
            //值得注意的是,这个过程是值拷贝,如果class比较复杂代价会比较大。(即:较消耗性能)
            //而且修改class的字段值不会同步到table,反过来也不会。此种方式可以通过把类型加到
            //GCPotimize生成减低开销。
            //得到lua中的表信息
            GameUser gameuser = env.Global.Get("GameUser");
            Debug.Log("GameUser.name=" + gameuser.name);
            Debug.Log("GameUser.age="+gameuser.age);
            Debug.Log("GameUser.ID="+gameuser.ID);
            gameuser.Walking();//调不出来
            gameuser.Speak();//调不出来
            Debug.Log(gameuser.Calulation(50, 30));//调不出来,值为0
        }

性能分析:

这个过程是值拷贝,修改Class的字段值不会同步到Lua中的table。如果class比较复杂代价会比较大(即:较消耗性能。)而且修改class的字段值不会同步到table,反过来也不会。此种方式可以通过把类型加到GCPotimize生成减低开销。

这里要说明的是,后面的方法没有映射到,没有得到Lua中table的方法,可能是我的写法不对,也可能是这种方法不能获得lua中Tbale的函数方法。

----------------- 2)映射到interface,引用拷贝(推荐方式)

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

注意点与性能分析:

A:接口需要添加特性标签[CSarpCallLua],否则无法生成实例代码

B:为引用拷贝,适合用在复杂表,一般商业项目推荐使用本方式

eg:

C#

[CSharpCallLua]
public interface IGameUser
{
    string name { get; set; }
    int age { get; set; }
    string ID { get; set; }
    void Speak();
    void Walking();
    int Calulation(int num1, int num2);
}
public class CsharpCallLuaTableByInterfaceComplex : MonoBehaviour
{
    //lua环境(官方建议全局唯一)
    LuaEnv env = null;
    private void Start()
    {
        env = new LuaEnv();
        //
        env.DoString("require 'CsharpCallLuaTable' ");
        //得到lua中的表信息
        IGameUser gameUser = env.Global.Get("GameUser");
        //输出显示
        Debug.Log("使用接口GameUser.name" + gameUser.ID);
        Debug.Log("使用接口GameUser.age" + gameUser.age);
        Debug.Log("使用接口GameUser.ID" + gameUser.ID);
        //输出调用方法
        gameUser.Speak();
        gameUser.Walking();
        int tmpResult = gameUser.Calulation(100, 200);
        Debug.Log("经过lua中计算,结果是"+tmpResult);
    }
}
----------------- 3)映射到xlua自身的LuaTable.

这种方式好处是不需要生成代码,但问题就是比较慢(即:效率低下),比interface方式要慢一个数量级,比如没有类型检查。

性能分析:
因为效率较低,所以不推荐常用,适合用在一些较为复杂且使用频率很低的情况下,所以不推荐使用。

eg:

C#

   public class CsharpCallLuaTableByLuaTable : MonoBehaviour
    {
        //lua环境(官方建议全局唯一)
        LuaEnv env = null;
        private void Start()
        {
            env = new LuaEnv();
            //
            env.DoString("require 'CsharpCallLuaTable' ");
            //得到lua中的复杂表信息
            XLua.LuaTable tabGameUser = env.Global.Get("GameUser");
            //输出显示
            Debug.Log("name=" + tabGameUser.Get("name"));
            Debug.Log("age=" + tabGameUser.Get("age"));
            Debug.Log("ID=" + tabGameUser.Get("ID"));
            //效率较低,适合用于一些较为复杂且使用频率很低的情况下,所以不推荐使用。
            //输出表中函数
            XLua.LuaFunction funSpeak = tabGameUser.Get("Speak");
            funSpeak.Call();
            XLua.LuaFunction funWalking = tabGameUser.Get("Walking");
            funSpeak.Call();
            XLua.LuaFunction funCalulation = tabGameUser.Get("Calulation");
            object[] objArray=  funCalulation.Call(tabGameUser,10,20);
            Debug.Log("输出结果="+objArray[0]);//输出结果
        }
    }

---- 3.全局方法函数function

两种方法,映射到委托和映射到Xlua自身的LuaFunction.

Lua

--无参函数
function ProcMyFunc1()
   print("ProcMyFunc1 无参函数")
end
--有参函数
function ProcMyFunc2(num1,num2)
  print("ProcMyFunc2 两个函数 num1+num2="..num1+num2)
end
---带返回值函数
function ProcMyFunc3(num1,num2)
  print("ProcMyFunc3 具备返回数值的函数")
  return num1+num2
end
--带有多个参数的函数
function ProcMyFunc4(num1,num2,num3)
   print("ProcMyFunc4 三个函数 num1+num2+num3="..num1+num2+num3)
end
--定义具有多个返回数值的函数
function ProcMyFunc5(num1,num2)
  local result=num1+num2
  print("ProcMyFunc5 函数,具备多个数值")
  return num1,num2,result
end

----------------- 1)映射到委托delegate(推荐)

优点:这是建议的方式,性能好很多,而且类型安全。

缺点:要生成代码。

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

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

注意:
1.含有out与ref关键字委托也需要添加特性标签[CSharpCallLua]。

2.委托引用后,退出luaEnv前,需要释放委托引用,否则lua报错。

3.对于Unity与C#中的复杂类型API,必须加入Xlua的配置文件,经过生成代码后才能正确应用。


共三步:

第一步声明委托:含有out与ref关键字委托也需要添加特性标签[CSharpCallLua]

//自定义委托(使用out或者关键字)
[CSharpCallLua]
public delegate void delAddingMutiReturnOut(int num1, int num2, out int res1, out int res2, out int res3);
[CSharpCallLua]
public delegate void delAddingMutiReturnRef(ref int res1, ref int res2, out int result);
//不含out和ref关键字不用加标签
public delegate void delAddings(int num1, int num2);

第二步在xlua的ExampleGenConfig配置文件注册委托类型

[CSharpCallLua]
public static List CSharpCallLua = new List() 
{
            typeof(Action),
            typeof(Action),新加的
            typeof(Func),新加的
            typeof(Func),
            typeof(Action),
            typeof(Action),
            typeof(UnityEngine.Events.UnityAction),
            typeof(System.Collections.IEnumerator)
 };

第三步在对应位置调用lua中函数。

   public class CsharpCallLuaFunctionByDelegateMultReturn : MonoBehaviour
    {
        //lua环境(官方建议全局唯一)
        LuaEnv env = null;
        //委托声明
        delAddingMutiReturnOut act1 = null;
        delAddingMutiReturnRef act2 = null;
        delAddings act3 = null;
        private void Start()
        {
            env = new LuaEnv();
            //不用加lua后缀
            env.DoString("require 'CsharpCallLuaTable' ");
            //得到lua中的具有多个返回数值的函数(通过委托out关键字来进行映射)
            act1 = env.Global.Get("ProcMyFunc5");
            //使用out
            //输出返回结果
            int intOutRes1 = 0;
            int intOutRes2 = 0;
            int intOutRes3 = 0;
            act1.Invoke(100, 880, out intOutRes1, out intOutRes2, out intOutRes3);
            Debug.Log(string.Format("使用out关键字测试多输出 res1={0},res2={1},res3={2}", intOutRes1, intOutRes2, intOutRes3));
            //使用ref
            act2 = env.Global.Get("ProcMyFunc5");
            //输出返回结果
            int intResult = 0;
            int intNum1 = 30;
            int intNum2 = 20;
            act2.Invoke(ref intNum1,ref intNum2,out intResult);
            Debug.Log(string.Format("使用ref关键字测试多输出 res1={0},res2={1},res3={2}", intNum1, intNum2, intResult));
            act3 = env.Global.Get("ProcMyFunc2");
            act3.Invoke(5000,500);
        }
        private void OnDestroy()
        {
            act1 = null;
            act2 = null;
            act3 = null;
            //释放LuaEnv
            env.Dispose();
        }
    }

----------------- 2)映射到LuaFunction

使用Xlua自身带有的LuaFunction.

优点:无需生成代码。

缺点:性能不高,不推荐。
这种方式的优缺点刚好和第一种相反。使用也简单。LuaFunction上由个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。

   public class CharpCallLuaFunctionByLuaFunction : MonoBehaviour
    {
        //lua环境(官方建议全局唯一)
        LuaEnv env = null;
        private void Start()
        {
            env = new LuaEnv();
            //不用加lua后缀
            env.DoString("require 'CsharpCallLuaTable' ");
            //得到lua中的函数信息(通过LuaFunction来进行映射)
            LuaFunction luaFun = env.Global.Get("ProcMyFunc1");
            LuaFunction luaFun2 = env.Global.Get("ProcMyFunc2");
            LuaFunction luaFun3 = env.Global.Get("ProcMyFunc3");
            //调用具有多返回数值
            LuaFunction luaFun4 = env.Global.Get("ProcMyFunc5");
            //输出
            luaFun.Call();
            luaFun2.Call(10, 20);
            object[] objArray = luaFun3.Call(30, 40);
            Debug.Log("调用ProcMyFunc3,结果=" + objArray[0]);
            object[] objArray2 = luaFun4.Call(22, 80);
            Debug.Log(string.Format("测试多返回数值 res={0},res2={1},res3={2}",objArray2[0],objArray2[1],objArray2[2]));
        }
        private void OnDestroy()
        {
            //释放LuaEnv
            env.Dispose();
        }
    }

三.LuaCallCsharp

lua调C#常见情况有(new)实例化,调用字段属性,方法,委托事件。

---- 1.实例化

基本方法:使用CS开头,实例化类。

eg:

C#

 UnityEngine.GameObject newGameObject=new UnityEngine.GameObject();

Lua

local newGameObject=CS.UnityEngine.GameObject()

基本规则:

A:Lua里头没有new关键字。

B:所有C#相关的都放到CS下,包括构造函数,静态成员属性,方法。

如果有多个构造函数呢?

xlua支持重载,比如你要调用GameObject的带一个string参数的构造函数,这么写:

local newGameObj2=CS.UnityEngine.GameObject('helloworld')

小技巧:
如果需要经常访问的类,可以先用局部变量引用后访问,除了减少敲代码的时间,还能提高性能。(即:lua写查找与得到组件等方式,推荐与C#中一样,尽量把查找与得到的组件进行缓存,节省系统开销。)

---- 2.访问类中的字段属性方法

eg:

c#

using UnityEngine;

namespace XluaPro
{
    public class LuaCallMyClass
    {
        private string name = "HER";
        public int Count = 5;
        public string str = "你好";

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public LuaCallMyClass(int sum)
        {
            Debug.Log("执行了构造函数LuaCallMyClass传入了数值:" + sum);
        }
        public void MyMethod()
        {
            this.Name = "gaimingle";
            Debug.Log("执行了LuaCallMyClass中的MyMethod方法");
        }
        public static void StaticMyMethod()
        {
            Debug.Log("执行了LuaCallMyClass中的StaticMyMethod方法");
        }
    }
}

Lua

local LuaCallMyClass= CS.XluaPro.LuaCallMyClass
local classObj=LuaCallMyClass(100)   --自动调用父类与子类的构造函数
local count= classObj.Count
local str=classObj.str
classObj:MyMethod()
classObj.MyMethod(classObj)
local name=classObj.Name
LuaCallMyClass.StaticMyMethod()
print("取得了c#中类的值count="..count.."  str="..str.."name="..name)

注意:

1.Lua中调用函数有两种方式:点和冒号。

冒号是一个语法糖,冒号(:)调用函数时,会默认将调用者自身作为第一个参数。 classObj:MyMethod()和classObj.MyMethod(classObj)是一样的。

3.什么时候用冒号,什么时候用点:

当调用实例化类函数使用冒号,那为什么调用c#实例化类函数要将自身传递进去?这个我的解释是C#实例化类函数方法内都可以使用this关键字。当你在lua中调用实例化类这个函数方法的时候,系统不知道你到底用没用this关键字,万一你用了,lua这边没把自身的值给this,那边this就会报空引用异常,所以不管你用没用,系统强制都把自身作为参数给传进去避免出错。静态方法内不能使用this关键字。

我这个强行解释怎么样?当然以后自身姿势水平越来越高的时候,能给一个更高大上的解释。总之,调用非静态函数要用冒号,表现在代码层面就是这个函数方法内部可以使用this。

---- 3.调用方法

调用方法上面不是说明了吗,这里怎么还有?因为C#中的方法花样五花八门,委托事件其实也算在其中。这里我们把它们一一列举出来。

-------- (1).调用父类属性与方法

xlua支持(通过派生类)访问基类的静态属性,静态方法,(通过派生类实例)访问基类的成员属性,成员方法。

using UnityEngine;

namespace XluaPro
{
	public class LuaCallCsharpClassFather
    {
        public string FatherName = "LuaCallCsharpClassFather";

        public LuaCallCsharpClassFather()
        {
            Debug.Log("这是父类的构造函数");
        }
        public void MyFatherClass()
        {
            Debug.Log("这是父类的函数LuaCallCsharpClassFather.MyFatherClass");
        }


    }
    public class LuaCallMyClass:LuaCallCsharpClassFather
    {
        private string name = "HER";
        public int Count = 5;
        public string str = "你好";

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public LuaCallMyClass(int sum)
        {
            Debug.Log("执行了构造函数LuaCallMyClass传入了数值:" + sum);
        }
        public void MyMethod()
        {
            this.Name = "gaimingle";
            Debug.Log("执行了LuaCallMyClass中的MyMethod方法");
        }
        public static void StaticMyMethod()
        {
            Debug.Log("执行了LuaCallMyClass中的StaticMyMethod方法");
        }
    }
    
}

Lua

local LuaCallMyClass= CS.XluaPro.LuaCallMyClass
local classObj=LuaCallMyClass(100)   --自动调用父类与子类的构造函数
classObj:MyFatherClass()--调用父类的方法
print("父类的字符串FatherName="..classObj.FatherName)--调用父类的字段
-------- (2).调用重载方法

xlua支持方法的重载,但为"有限重载".直接通过不同的参数类型进行重载参数的访问。
xlua只一定程度上支持重载函数的调用,因为lua的类型远远不如C#丰富,存在一对多的情况。比如C#int,float,double都对应于lua的number。所以如果重载类型是int和float则无法区分,只能调用到其中一个(生成代码中排前面的那个)
eg:

using UnityEngine;

namespace XluaPro
{
    public class LuaCallMyClass:LuaCallCsharpClassFather
    {
        private string name = "HER";
        public int Count = 5;
        public string str = "你好";

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public LuaCallMyClass(int sum)
        {
            Debug.Log("执行了构造函数LuaCallMyClass传入了数值:" + sum);
        }
        public void MyMethod()
        {
            this.Name = "gaimingle";
            Debug.Log("执行了LuaCallMyClass中的MyMethod方法");
        }
        public static void StaticMyMethod()
        {
            Debug.Log("执行了LuaCallMyClass中的StaticMyMethod方法");
        }
        public void OverLoadMehtod(int a,int b)
        {
            Debug.Log("执行了OverLoadMehtod(int类型)方法");
        }
        public void OverLoadMehtod(float a, float b)
        {
            Debug.Log("执行了OverLoadMehtod(浮点类型)方法");
        }
        public void OverLoadMehtod(string a, string b)
        {
            Debug.Log("执行了OverLoadMehtod(字符串)方法");
        }
    }
}

Lua

local LuaCallMyClass= CS.XluaPro.LuaCallMyClass
local classObj=LuaCallMyClass(100)   --自动调用父类与子类的构造函数
classObj:OverLoadMehtod(60.9,70.9)   --lua无法区分整型和浮点型,哪个写在前面执行哪个,这里执行的是整型函数
classObj:OverLoadMehtod(60,70)   
classObj:OverLoadMehtod("aaa","BBB")
-------- (3).调用带参方法

各种奇怪的参数一起来吧。

----------------- 1)调用可变参数方法

C#

 //可变参数
public void MyMethodParams(int a, int b, params string[] strArrary)
{
    Debug.Log("a="+a+"b="+b+ string.Join("", strArrary));
}

Lua

local LuaCallMyClass= CS.XluaPro.LuaCallMyClass
local classObj=LuaCallMyClass(100)
classObj:MyMethodParams(20,30,"ABC","EFG","HIGK")
----------------- 2)调用参数有结构体的方法

Lua使用一个表,来映射C#的结构体

 public struct MyStruct
 {
        public string x;
        public string y;
 }
 public void MyMethodStruct(MyStruct myStruct)
 {
        Debug.Log("myStruct.x="+myStruct.x+ " myStruct.y="+myStruct.y);
 }

Lua

local LuaCallMyClass= CS.XluaPro.LuaCallMyClass
local classObj=LuaCallMyClass(100)
 mystruct=
 {
   x="abc",
   y="efg"
 }
 classObj:MyMethodStruct(mystruct)
----------------- 3)调用参数有接口的方法

lua使用一个表,来映射c#的接口。

注意:接口需要加入标记:[CSharpCallLua]

C#

   //定义接口
    [XLua.CSharpCallLua]
    public interface MyInterFace
    {
        int x { get; set; }
        int y { get; set; }
       public void Speak();
    }
   public void MyInterFaceMethod(MyInterFace myInterface)
    {
     Debug.Log("MyInterFace.x="+myInterface.x+ " MyInterFace.y="+ myInterface.y);
     myInterface.Speak();
    }

Lua

local LuaCallMyClass= CS.XluaPro.LuaCallMyClass
local classObj=LuaCallMyClass(100)
--用于映射接口的表
myInterface=
{
  x=100,
  y=200,
  Speak=function()
  print("执行了myInterface里面的Speak方法");
  end
}
--执行该方法
classObj:MyInterFaceMethod(myInterface)
----------------- 4)调用参数有委托的方法

lua使用一个函数,来映射C#的委托。

委托需要加入标记[CSharpCallLua] 这里不管你有没有带ref,out关键字都要加

eg:


C#

    [XLua.CSharpCallLua]
    public delegate void MyDelegate(int num);
    public void MyDelegateMethod(MyDelegate mydelegate)
    {
        mydelegate.Invoke(99);
    }

Lua

local LuaCallMyClass= CS.XluaPro.LuaCallMyClass
local classObj=LuaCallMyClass(100)
myDelegate=function(num)
num=num+100
print("执行了myDelegate方法,并且num的值为"..num)
end
classObj:MyDelegateMethod(myDelegate)
-------- (4).调用具有多返回值方法

基本规则:参数的输入输出属性(out,ref)

A:C#的普通参数算一个输入形参,ref修饰的算一个输入形参,out不算,然后从左往右对应lua调用的实参列表。

B:Lua调用返回值处理规则:

C#函数的返回值(如果有的话)算一个返回值,out算一个返回值,ref算一个返回值,然后从左往右对应lua的多返回值。

C#

 public int MultReutrnMehtod(ref int a,out int b,ref int c,int d,out int  e)
    {
        b =20;
        e = 50;
        return a + b + c + d + e;
    }

Lua

local LuaCallMyClass= CS.XluaPro.LuaCallMyClass
local classObj=LuaCallMyClass(100)
 returnResult,a,b,c,e= classObj:MultReutrnMehtod(10,30,40)
print("a="..a.." b="..b.." c="..c.." e="..e.." returnResult="..returnResult)

注意:执行结果返回值是第一个,然后依据out,ref按顺序得到多返回值。
lua中的参数个数从左到右out忽略,上面例子在lua中实际上只有三个参数。

-------- (5).调用泛型方法

基本规则:
lua不直接支持C#的泛型方法,但可以通过扩展方法功能进行封装后调用。
使用Extension methods(扩展方法)技术就是C#中在不改变原始类的基础上,使用一种机制可以无限扩展这个类功能的机制。

Eg:原始类为:A 扩展类为:Ext_A

注意事项:
A:Ext_A 必须是一个静态类,且扩展方法也必须是静态的。方法的参数中必须要有被扩展类作为其中一个参数,此参数前面必须有this关键字修饰。

B:lua直接调用扩展方法,需要给"泛型类"与"扩展方法类",都加入特性标记:[xlua.LuaCallCSharp]

---- 4.调用委托事件

这里我有个疑问,我在C#中定义一个委托字段,然后在Lua中给这个委托字段添加委托链,并且在lua执行这个委托链。我自己试了下不行。可能是我的写法有问题。这里我在C#单独写一个添加方法的方法函数和一个执行的方法函数。
eg:
C#

  //定义委托
[XLua.CSharpCallLua]
public delegate void MyDelegatestr(string str);

public MyDelegatestr mydelegateMehods;
public void AddAction(MyDelegatestr action)
{
    this.mydelegateMehods += action;
}
public void InvokemydelegateMehods()
{
    mydelegateMehods.Invoke("我来测试来了");
}

Lua

--委托链方法一
myDelegate1=function(num)
print("执行了方法myDelegate1"..num)
end
--方法二
 myDelegate2=function(num)
print("执行了方法myDelegate2"..num)
end
--方法三
myDelegate3=function(num)
print("执行了方法myDelegate3"..num)
end
local LuaCallMyClass= CS.XluaPro.LuaCallMyClass
local classObj=LuaCallMyClass(100)   

--使用LuaCallMyClass类的对象访问委托变量action
classObj:AddAction(myDelegate1)
classObj:AddAction(myDelegate2)
classObj:AddAction(myDelegate3)
--执行委托链
classObj:InvokemydelegateMehods()

事件就是加个关键字,只能在外部绑定,不能在外部执行。没啥可说的。这个以后发现了解决办法会回来更新

三.其他注意事项

1:lua调用C#,需要在Xlua中生成"适配代码"。则在这个类打入一个[LuaCallCsharp]的标签。

2.如果lua调用C#的系统API,则无法拿到源代码,无法打入标签。则使用"静态列表"方式解决。

eg:
Public static List mymodule_LuaCallCS_List=new List()
{
typeof(GameObject),
typeof(Dictionary),
};
然后把以上代码放入一个静态类中即可。

3.实际开发过程中,lua调用C#用的比较多。
xlua的优点体现的没有必要每次改的时候,都要生成代码。主要原理是依赖于编译器环境下,
利用反射来动态生成代码接口。

4:在标有"[XLua.LuaCallCsharp]"的C#类中,添加新的方法后,如果是生成了代码类,则必须重新生成或者删除,否则XLua用以前生成的,进行注册查询,会出现Lua异常:“视图访问一个nil的方法”


还有很多问题,先整理到这里,慢慢来。

你可能感兴趣的:(Xlua中lua语言与C#之间的调用)