xLua學習筆記(二) C#調用Lua代碼


獲取全局變量

只需要調用LuaEnv對象Global屬性的Get方法即可

LuaTable Global;
  • 描述:
    代表lua全局環境的LuaTable
T Get<T>(string key); 
  • 描述:
    獲取在key下,類型為T的value,如果不存在或者類型不匹配,返回null;

例如有如下Lua代碼

number = 1 string = "hello world" boolean = true 

在C#中嘗試用上述方法輸出Lua中的全局變量number,string和boolean

Debug.Log("number = " + luaenv.Global.Get<int>("number")); Debug.Log("string = " + luaenv.Global.Get<string>("string")); Debug.Log("boolean = " + luaenv.Global.Get<bool>("boolean")); 

得到結果

 
 

如果需要獲取Lua中Table的數據,則需要將Table映射為C#中相對應的數據結構,可選的方式有:classinterfaceDictionaryListLuaTable

假設有如下的Table

table =
{
    f1 = 1,
    f2 = 2
}

如果要使用class做映射,只需要在C#中定義一個相對應的類即可,注意變量名稱要和Table中的名稱相同,xLua會依次在table中尋找是否有與class中變量名相同的key值,如果有則將其value值復制到對應的變量上,如果在Table的Key值中找不到與定義的class的變量,則變量會被賦於對應類型的初值

class Table { public int f1; public int f2; } 

獲取變量的方法還是和之前一樣

Table table = luaenv.Global.Get<Table>("table"); Debug.Log("table.f1 = " + table.f1); Debug.Log("table.f2 = " + table.f2); 

得到結果

 
 

同樣地,還可以使用DictionaryLuaTable(速度比較慢)來映射Table,代碼如下

Dictionary<string, int> dict = luaenv.Global.Get<Dictionary<string, int>>("table"); Debug.Log("dict[f1] = " + dict["f1"]); Debug.Log("dict[f2] = " + dict["f2"]); LuaTable luaTable = luaenv.Global.Get<LuaTable>("table"); Debug.Log("luaTable.Get<int>(\"f1\") = " + luaTable.Get<int>("f1")); Debug.Log("luaTable.Get<int>(\"f2\") = " + luaTable.Get<int>("f2")); 

得到結果

 
 
 
 

對於下面這種Table,可以使用List來做映射

table =
{
    1,
    2
}
List<int> list = luaenv.Global.Get<List<int>>("table"); Debug.Log("list[0] = " + list[0]); Debug.Log("list[1] = " + list[1]); 

得到結果

 
 

如果Table中存在函數,例如

table =
{
    f1 = 1, f2 = 2, add = function(self, num1, num2) return num1 + num2 end } 

可以使用interface來映射,對於上述的Table,可以聲明如下ITable接口

使用這種方法讀取Table時需要生成代碼,所以必須要給接口加上一個Attribute:[CSharpCallLua]

[CSharpCallLua] interface ITable { int f1 { get; set; } int f2 { get; set; } int add(int num1, int num2); } 
ITable iTable = luaenv.Global.Get<ITable>("table"); Debug.Log("table.f1 = " + iTable.f1); Debug.Log("table.f2 = " + iTable.f2); Debug.Log("table.add(1, 2) = " + iTable.add(1, 2)); 

得到結果

 
 

獲取全局函數

一般來說,全局函數的映射方式有兩種,一種是使用delegate(要生成代碼,性能好),另一種是使用LuaFunction(不用生成代碼,性能較差)。

假設有下列的全局函數

function action0() print ("action0() called!") end 

因為使用delegate的方式映射全局函數需要生成代碼,所以必須要添加一個Attribute:[CSharpCallLua]

[CSharpCallLua] private delegate void Action0(); 

獲取的方式大同小異

Action0 action0 = luaenv.Global.Get<Action0>("action0"); action0(); 

得到結果

 
 

當全局函數有一個或多個返回值的時候,按照在函數中的返回順序,從左到右依次對應到delegate返回值ref/out參數

假設有下列的全局函數

function action2(param1, param2) print("param1:", param1, " param2:", param2) return 1, {f1 = 2} end 

那么下面六種delegate的映射方式是等效的,

[CSharpCallLua] private delegate int Action2(int param1, int param2, out Table table); [CSharpCallLua] private delegate int Action2_(int param1, int param2, ref Table table); [CSharpCallLua] private delegate void Action2__(int param1, int param2, out int val ,out Table table); [CSharpCallLua] private delegate void Action2___(int param1, int param2, ref int val, ref Table table); [CSharpCallLua] private delegate void Action2____(int param1, int param2, ref int val, out Table table); [CSharpCallLua] private delegate void Action2_____(int param1, int param2, out int val, ref Table table); 

delegate的返回值也可以是delegate,假設有下列的全局函數

function action0() print ("action0() called!") end function get_action0() print("get_action0() called!") return action0 end 

先定義Action0,然后將GetAction0的返回值設為Action0即可

[CSharpCallLua] private delegate void Action0(); [CSharpCallLua] private delegate Action0 GetAction0(); 
GetAction0 getAction0 = luaenv.Global.Get<GetAction0>("get_action0"); (getAction0())(); 

運行得到結果

 
 

使用LuaFunction來映射全局函數的方法與使用delegate時基本相同,映射后如果要在C#中使用函數,只需要調用LuaFunction對象的Call方法即可,Call方法有下面兩種重載形式

object[] Call(params object[] args) 
  • 描述:
    以可變參數調用Lua函數,並返回該調用的返回值。
object[] Call(object[] args, Type[] returnTypes) 
  • 描述:
    調用Lua函數,並指明返回參數的類型,系統會自動按指定類型進行轉換。

需要注意的是,當函數有返回值的時候,最好指明返回參數的類型,如果沒有指明類型,在將object對象轉換具體類型的時候可能會InvalidCastException異常

假設有下面的全局函數

function action2(param1, param2) print("param1:", param1, " param2:", param2) return 1, {f1 = 2} end 

下面的C#代碼使用LuaFunction調用該全局函數

LuaFunction luaFunction = luaenv.Global.Get<LuaFunction>("action2"); object[] vals = luaFunction.Call(new object[]{1, 2}, new Type[] { typeof(int), typeof(Table) }); Debug.Log("vals[0] = " + (int)vals[0]); Debug.Log("vals[1] = " + ((Table)vals[1]).f1); 

得到結果

 
 

LuaFunction訪問Lua函數的過程涉及到了多次的裝箱與拆箱操作,雖然不用生成代碼,但是性能的消耗是比較大的

由於訪問Lua全局數據,特別是table以及function,代價比較大,推薦使用單獨的模塊負責管理其加載,而不是每次用之前去加載

資源釋放

下面這段這段C#代碼首先將Lua函數test映射到了委托action上,使用完成后調用了LuaEnv的Dispose方法回收了LuaEnv對象

public class SpecialCase : MonoBehaviour { [CSharpCallLua] public delegate void Action(); private LuaEnv luaenv; private Action action; void Start () { luaenv = new LuaEnv(); luaenv.DoString ( @" function test() print('test') end " ); action = luaenv.Global.Get<Action>("test"); luaenv.Dispose(); } } 

運行上面這段代碼,發現拋出了如下的異常

 
 

查閱FAQ

調用LuaEnv.Dispose時,報“try to dispose a LuaEnv with C# callback!”錯是什么原因?

這是由於C#還存在指向lua虛擬機里頭某個函數的delegate,為了防止業務在虛擬機釋放后調用這些無效(因為其引用的lua函數所在虛擬機都釋放了)delegate導致的異常甚至崩潰,做了這個檢查

怎么解決?釋放這些delegate即可,所謂釋放,在C#中,就是沒有引用

你是在C#通過LuaTable.Get獲取並保存到對象成員,賦值該成員為null

你是在lua那把lua函數注冊到一些事件事件回調,反注冊這些回調

如果你是通過xlua.hotfix(class, method, func)注入到C#,則通過xlua.hotfix(class, method, nil)刪除

要注意以上操作在Dispose之前完成

因此,只需要在LuaEnv釋放之前,將Lua函數映射的委托變量action設置為null即可

action = luaenv.Global.Get<Action>("test"); action = null; luaenv.Dispose();


作者:BlauHimmel
鏈接:https://www.jianshu.com/p/8dde7be41d96
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM