1. 程式人生 > >XLua 學習之路(二)之C#訪問Lua

XLua 學習之路(二)之C#訪問Lua

       這裡指的是C#主動發起對Lua資料結構的訪問。從Lua支援的資料型別來講,C#獲取Lua元素主要分為:基本資料型別(number,bool,string),複雜的資料型別(table),函式(function)。

       本次測試的待獲取的Lua檔案CSharpCallLua.lua.txt內容t如下:

--用於測試獲取全域性基本資料型別
a = 100;--全域性變數
isDead = false
str = "leander"
--用於測試獲取全域性函式型別
function addTest()
	print("addTest")
end
--用於測試獲取全域性函式帶引數型別
function add(a,b)
	print(a+b)
end
--用於測試獲取全域性函式帶多個返回值型別
function addReturnMulti(a,b)
	return a+b,a,b
end
--用於測試獲取全域性table資料型別
preson={
	name = "leander",2,"test",3.3,
	age = 24,
	--self引數,代表當前table物件
	sum = function(self,a,b)
		print("a+b="+(a+b))
	end

}

--使用“:”定義
--預設帶一個self引數,代表當前呼叫物件

--[
--用於測試獲取全域性table外部定義函式(不顯式第一個引數)型別
function preson:sum(a,b)
	print(a+b)
end
--]

--使用“.”定義
--[
--用於測試獲取全域性table外部定義函式(顯式第一個引數)型別
function preson.sum(self,a,b)
	print(a+b)
end
--]

###一、獲取全域性基本資料型別
       首先,還是建立XLua的虛擬機器。然後,再通過LuaEnv.Global.Get<型別>(“name”)方法進行獲取即可;示例程式碼如下:

LuaEnv env = new LuaEnv();//建立Lua虛擬機器
//讀取CSharpCallLua.lua.txt檔案
env.DoString("require 'CSharpCallLua'");

//C#獲取lua的基本資料型別
int a = env.Global.Get<int>("a");
bool isDead = env.Global.Get<bool>("isDead");
string str = env.Global.Get<string>("str");

print(a); //100
print(isDead); //False
print(str); //leander
env.Dispose(); //釋放Lua虛擬機器

###二、獲取全域性table型別
       獲取table型別,一共有四種方式:1.將table對映到普通class或struct 2.對映到介面(優先考慮)3.對映到Dictionary或List 4.通過XLua的LuaTable完成對映;下面將分別完成這四種的對映。
####1.對映到普通class或struct
       這種方式下xLua會幫你new一個例項,並把對應的欄位賦值過去。table的屬性可以多於或者少於class的屬性。可以巢狀其它複雜型別。要注意的是,這個過程是值拷貝,如果class比較複雜代價會比較大。而且修改class的欄位值不會同步到table,反過來也不會。這個功能可以通過把型別加到GCOptimize生成降低開銷。實現方式如下:
       首先,構建table對應的class。

//第一種對映方式
class Preson
{
	//欄位名要與lua檔案table中保持一致
    public string name;
    public int age;
}

       再通過LuaEnv.Global.Get<型別>(“name”)方法進行獲取即可;示例程式碼如下:

//要注意的是,這個過程是值拷貝,如果class比較複雜,代價會比較大。
//而且修改class的欄位值不會同步到table,反過來也不會。
Preson p = env.Global.Get<Preson>("preson");
print(p.name + ":" + p.age);//leander:24
p.name = "leander.com";
env.DoString("print(preson.name)");//leander 注意:此處並不是leander.com

       注意:此方法,不能對映table中的函式,若要對映到table中的函式,則考慮第二種對映方式
####2.對映到介面
       這種方式依賴於生成程式碼(如果沒生成程式碼會拋InvalidCastException異常),程式碼生成器會生成這個interface的例項,如果get一個屬性,生成程式碼會get對應的table欄位,如果set屬性也會設定對應的欄位。甚至可以通過interface的方法訪問lua的函式。實現方式如下:
首先,構建table對應的interface。

//第二種對映方式
[CSharpCallLua] //如果不加這個屬性就會拋InvalidCastException異常
interface IPerson
{
    //介面不能包含欄位,將其轉換成屬性
    string name { get; set; }
    int age { get; set; }

    //函式名稱需要與lua檔案函式名一致
    void sum(int a,int b);
}

       再通過LuaEnv.Global.Get<型別>(“name”)方法進行獲取即可;示例程式碼如下:

//2.對映到介面(優先考慮)
//這個過程是引用拷貝,修改IPerson的屬性會同步到table
IPerson p = env.Global.Get<IPerson>("preson");
print(p.name); //leander
p.name = "leanderQT";
env.DoString("print(preson.name)");//leanderQT 注意:此時name的value被更新了
p.sum(12, 32); //44 相當於p.Sum(p,12,32);

       注意:對映class和struct和對映到interface的區別在於:1.對映class和struct是值拷貝,而對映到interface為引用拷貝。2.對映到interface時,需要新增[CSharpCallLua]註解,用於XLua生成程式碼。
####3.對映到Dictionary或List
       不想定義class或者interface的話,可以考慮用這個,前提table下key和value的型別都是一致的。

//3.對映到Dictionary 或 List

//List只能對映值,無法對映key-value形式 比如:name:leander age:24 函式等
List<object> list = env.Global.Get<List<object>>("preson");

foreach (object o in list)
{
    print(o);
}//輸出 2 "test" 3.3

//Dictionary只能對映key-value形式,無法對映值,比如:數字,字串等
Dictionary<string, object> dict = env.Global.Get<Dictionary<string, object>>("preson");

foreach (string key in dict.Keys)
{
    print(key + ":" + dict[key]);
}//輸出 name:leander age:24 sum:function:11

       注意:List只能對映值,無法對映key-value形式;Dictionary只能對映key-value形式,無法對映值形式
####4.通過XLua的LuaTable完成對映
       這種方式好處是不需要生成程式碼,但也有一些問題,比如慢,比方式2要慢一個數量級,比如沒有型別檢查。

//4.通過XLua的LuaTable完成對映 只能獲取table中的key-value形式(包括函式)
//若table中包含其他非key-value形式,下面程式碼將會報InvalidCastException異常
//在測試這段程式碼時,需要將lua檔案的table裡面2,"test",3.3移除,因為這種屬於非key-value形式
LuaTable tab = env.Global.Get<LuaTable>("preson");
print(tab.Get<string>("name")); //leander
print(tab.Get<int>("age")); //24
print(tab.Length); //獲取table中非key-value形式的元素個數
foreach (string key in tab.GetKeys())
{
    print(tab.Get<object>(key));
}//輸出 leander 24 0

       此處的函式,被對映成0了,原因還在排查…
###三、獲取全域性函式
仍然是用Get方法,不同的是型別對映。獲取function,一共有兩種方式:1、對映到delegate 2.對映到LuaFunction。
####1.對映到delegate
       這種是建議的方式,效能好很多,而且型別安全。缺點是要生成程式碼(如果沒生成程式碼會拋InvalidCastException異常)。示例程式碼如下:
       首先,定義函式對應的委託型別。

 [CSharpCallLua]
 //無返回值時委託定義
 delegate void Add(int a,int b);
 [CSharpCallLua]
 //多個返回值時委託定義 除去該方法第一個返回值時,使用out/ref關鍵字來接收
 delegate int AddReturnMulti(int a, int b, out int resa,out int resb);

       然後還是基於Get方法。

//訪問lua中的全域性函式 對映到delegate
//無參型別
Action addTest = env.Global.Get<Action>("addTest");
addTest(); //addTest
//在釋放該虛擬機器之前,要先釋放上述對映的全域性函式
addTest = null;

//帶參型別 1.通過自定義委託型別來實現對映
Add add_Params = env.Global.Get<Add>("add");
add_Params(1, 3);//4
add_Params = null;

//多個返回值
AddReturnMulti addReturnMulti = env.Global.Get<AddReturnMulti>("addReturnMulti");
int resa, resb;
int result = addReturnMulti(34, 39, out resa, out resb);
print(resa + "+" + resb + "=" + result);//34+39=73
addReturnMulti = null;

####2.對映到LuaFunction
       LuaFunction上有個變參的Call函式,可以傳任意型別,任意個數的引數,返回值是object的陣列,對應於lua的多返回值。

LuaFunction func = env.Global.Get<LuaFunction>("addReturnMulti");
object[] os = func.Call(1,2);
foreach (object o in os)
{
    print(o);
}//3 1 2

###四、小結
       1、訪問lua全域性資料,特別是table以及function,代價比較大,建議儘量少做,比如在初始化時把要呼叫的lua function獲取一次(對映到delegate)後,儲存下來,後續直接呼叫該delegate即可。table也類似。
       2、如果lua測的實現的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一個專門的模組負責xlua的初始化以及delegate、interface的對映,然後把這些delegate和interface設定到要用到它們的地方。

相關推薦

XLua 學習C#訪問Lua

       這裡指的是C#主動發起對Lua資料結構的訪問。從Lua支援的資料型別來講,C#獲取Lua元素主要分為:基本資料型別(number,bool,string),複雜的資料型別(table),函式(function)。        本次測試的待獲取的L

Android外掛化學習ClassLoader完全解析

Java程式碼都是寫在Class裡面的,程式執行在虛擬機器上時,虛擬機器需要把需要的Class載入進來才能建立例項物件並工作,而完成這一個載入工作的角色就是ClassLoader。 類載入器ClassLoader介紹 Android的Dalvik/ART虛擬

Java學習流程控制語句

循環 cas 學習之路 將不 乘法表 length 跳出循環 spa int if、if…else…語句 if (true) { System.out.println("為真時執行");

python學習 -- 函數、JSON、終端樣式

blog ade def 數量 通過 等於 name tuple args 函數 函數構成 定義函數:使用def即可 def __getName(idCard): return user_info[idCard].Name 其中,__get

Python 學習

在外 封裝 過程 數列 == 3.6 開頭 res form Python 學習之路(二) 以下所用的是Python 3.6 一、條件語句 簡單判斷 1 if 判斷條件: 2 執行語句…… 3 else: 4 執行語句…… 復雜判斷 1 if 判斷

Hadoop學習Hadoop發展背景

chukwa 站點 avro azkaban das 可擴展性 對數 就是 pro Hadoop產生的背景 1. HADOOP最早起源於Nutch。Nutch的設計目標是構建一個大型的全網搜索引擎,包括網頁抓取、索引、查詢等功能,但隨著抓取網頁數量的增加,遇到了嚴重的可擴

C++再學習

iter pointer lin clas 數組元素 對數 表達 自增 條件操作符 1. 移位操作符“ << ”和“ >> ”擁有中等優先級:其優先級比算術操作符低,但比關系操作符、賦值操作符和條件操作符優先級高 2. *iter++   後自增操作

HBase學習 HBase集群安裝

star java_home 服務 blog usr mirrors logs 技術 ron 前提 1、HBase 依賴於 HDFS 做底層的數據存儲 2、HBase 依賴於 MapReduce 做數據計算 3、HBase 依賴於 ZooKeeper 做服務協調 4

Hive學習 Hive安裝

different 0.10 director lar blog cut cti mysql extend Hive的下載 下載地址http://mirrors.hust.edu.cn/apache/ 選擇合適的Hive版本進行下載,進到stable-2文件夾可以看到穩

Spark學習 Spark2.3 HA集群的分布式安裝

serve html 元數據 不安裝 rec ive cut 再次 apps 一、下載Spark安裝包 1、從官網下載 http://spark.apache.org/downloads.html 2、從微軟的鏡像站下載 http://mirrors.hust.

學習淺談:bash及其特性,命令歷史以及用戶管理及權限,shell的類型

bash 管理權限 過了一周了,進度似乎有點懈怠,不過過了周末重整旗鼓啦shell(外殼)GUI:Gnome,KDE,xfceCLI:sh,csh,ksh,bashbash(父進程)-----bash(子進程)他們相互獨立彼此不知命令歷史:historybash支持的引號:‘ ’命令替換(鍵盤~的按鍵

小強的Hadoop學習

com TE 區別 截斷 用戶 分開 路徑問題 登陸用戶 學習 接著第一遍。中間間隔了大約半年的時間了,話不多說,直接進入主題。 這篇是主要是應用篇。目前的環境是4臺機器 ,環境 centos 7.2 CDH5.10.2 網上很多安裝教程,這邊就不說明了。 Hive+

XML學習

req 方式 test 無符號 規範 內容 了解 實體 文本 DTD 一、什麽是DTD? DTD即Document Type Definition,文檔類型定義。 我們知道,XML的標簽可以自定義,不受任何約束。但有時侯,為了符合邏輯和業務需要,我們需要對XML文檔加以約束

Linux 學習:使用者及許可權詳解

作業: 1.ls 命令是否可以顯示某目錄的整體大小,即包括其內部的所有檔案的整體大小? 可以,使用ls -s xxx 2.通過幫助手冊,學習使用du命令: # du 估計檔案空間使用量 ​ -s 分割資料夾,不包括子目錄大小 ​ -h 以可讀格式展示

Tecnomatix Plant Simulation 14 學習

  本篇部落格主要介紹基礎遺傳演算法的實現,例子參考部落格一推薦的周金平老師教材第三章,這裡做簡單介紹   問題描述: 將n個裝置(M1,…Mn)放置到n個位置(A,B…)上,其中每個位置上能且僅能放置一臺裝置,已知n個裝置兩兩之間的物料搬運量大小W(i, j),以及

Kafka學習 Kafka的架構

most 工具 路由 冪等 用戶 toc 目標 支持 mem 一、Kafka的集群架構 如上圖所示,一個典型的Kafka集群中包含若幹Producer(可以是web前端產生的Page View,或者是服務器日誌,系統CPU、Memory等),若幹broker(Kafka支持

Flume學習 Flume的Source類型

bash 官方 tip size pipes lte using exc 正常 一、概述 官方文檔介紹:http://flume.apache.org/FlumeUserGuide.html#flume-sources 二、Flume Sources 描述 2.1 Avro

Weex學習除錯篇

寫在前面:作為前端開發工作者,一款好的除錯工具必不可缺。你可能已經習慣了chorme提供的簡單好用的網頁除錯工具,但在weex中,除錯是一個巨坑!! Weex除錯踩坑之旅 在weex中,除錯是一件非常麻煩但事,好在weex官方文件中提供了一些方案 1. weex-toolkit命

PKI學習-----------------------淺談socket

首先為什麼要用socket? 如果沒有socket,我們傳輸資料需要藉助TCP/IP協議,不僅需要三次握手,還要友好分手,每次傳輸都要經過複雜的連線,具體傳輸過程中,也會有一大堆的問題,什麼滑動視窗,什麼累計確認,分組快取,流量控制,,,聽著就頭皮發麻,而socket就是TCP/IP的實現

Netty學習-非同步IONIO程式設計

NIO到底是什麼簡稱?有人稱之為New I/O,原因為他相對於之前的I/O類庫來說是新增的。這是官方叫法。但是,由於之前老的I/O類庫是阻塞I/O,New I/O類庫的目標就是讓java支援非阻塞I/O,所以更多的人稱之為非阻塞I/O(Non-block I/O)。在開始進行NIO程式設計之