前言
在進行lua方法註冊的時候, 大多數解決方案直接否定了泛型方法, 因為在lua側難以表達出泛型, 以及lua的函式過載問題,
函式過載問題可以通過一些特殊方法解決, 而泛型問題是主要問題, 以unity + slua的情況來說
比如下面的類:
public class foo
public static void gettypename()
}一般只會生成gettypename(system.type type)的註冊方法.
那麼泛型的方法在lua那邊該怎樣註冊才能讓這個呼叫能夠實現呢? 一般來說我們呼叫泛型方法必須在寫**的時候就確定, 像這樣:
foo.gettypename(); // 輸出 int32
而lua並不能這樣約束, 它的呼叫必須還是非泛型的才可以, 這是第乙個問題, 而第二個問題是lua那邊怎樣寫? 我們希望它的寫法能跟c#保持
一致, 或者相似吧, 讓人看起來容易明白, 可是lua中中括號是大於小於號, 不能這樣寫, 想想有沒有什麼辦法
因為在lua中是沒有型別的, 型別必須來自c#, 所以只能將泛型作為非泛型方法才能使用, 如果讓函式進行一次退化和封裝, 像下面這樣
-- 先將c# 程式設計客棧的typeof註冊成全域性函式, 註冊system.int32命名為int
local foo = {}
foo.gettypename = function(type)
return function()
print(type.name)
endend
foo.gettypename(typeof(int))(); -- lua
foo.gettypenamewww.cppcns.comypeof(int)>(); // c#
這樣寫的話, 除了尖括號, 基本就能兩邊一致了對吧, 執行結果也是一樣的
/*至於怎樣註冊typeof(int)*/
// 在luastate的init中註冊個全域性函式[monopinvokecallbackattribute(typeof(luacsfunction))]
internal static int gettype(intptr l)
// 在luastate的init中自己註冊咯luadll.lua_pushcfunction(l, gettype);luadll.lua_setglobal(l, "typeof");
// customexport.onaddcustomclass 中新增型別別名
add(typeof(system.int32), "int"); // int
只是這裡lua的函式沒有進行c#那邊的呼叫啊, 下一步就來看看有沒有什麼辦法來實現呼叫.
如果通過自動註冊的話, foo應該是乙個已經註冊的型別.
[slua.customluaclass]
public class foo
並且有元表, 元表裡面有非泛型的gettypename方法了. 現在先不要去動元表,
直接註冊這個到table裡面, 因為如果table裡面有值的話, 就不會去查詢元表了
import "foo";
foo.gettypename(typeof(int)); // 輸出 int32
rawset(foo, "gettypename", function(type)
return function()
local mt = getmetatable(foo)
local func = rawget(mt,"gettypename");
func(type)
endend)
foo.gettypename(typeof(int))(); // 輸出 int32 -- 注意返回了function然後再次呼叫
這個方法比較流氓, 因為直接預設了有非泛型函式, 並且覆蓋了元表的非泛型方法, 不可取的.
要繼續的話, 首先來看看乙個泛型方法怎樣通過type方法進行呼叫的:
var methods = typeof(foo).getmethods(bindingflags.public | bindingflags.static | bindingflags.invokemethod);
foreach(var method in methods)
);if(genericmethod != null)}}
}當然是反射啦, 這樣就能讓泛型方法退化為非泛型了, 雖然是乙個緩慢的反射, 不過時間基本只花費在invoke上, 問題還不大.
剩下的問題是過載了, 有非泛型和泛型的兩個同名函式, 為了測試我先刪除掉非泛型,
[slua.customluaclass]
public class foo
public static void gettypename()
}生成的lua註冊**也要修改一下
system.type a1;
checktype(l,1,out a1);
foo.gettypename(a1); // 它完了
pushvalue(l,true);
改成system.type a1;
checktype(l,1,out a1);
var methods = typeof(foo).getmethods(system.reflection. bindingflags.public
| system.reflection.bindingflags.static
| system.reflection.bindingflags.invokemethod);
foreach(var method in methods)
);if(genericmethod != null)}}
}pushvalue(l,true);
試試執行一下看看, 輸出 int32 看來沒有問題, 問題是在lua那邊還是需要手動封裝了一遍:
rawset(foo, "gettypename", function(type)
local mt = getmetatable(foo)
local func = rawget(mt,"gettywfdylpename");
func(type)
end)
-- 問題是, 不進行一次rawset無法得到泛型寫法
foo.gettypename(typeof(int)); // 輸出 int32 -- table方法
到這裡, 基本就可以得出結論了,
一. 在lua中可以通過封裝(閉包)的方式接近c#的泛型的寫法, 差別只是乙個中括號和小括號
foo.gettypename(typeof(int))(); -- lua
foo.gettypename(); // c#
然而過程異常複雜, 比如上述**中的rawset過程需要在c#的註冊**中進行實現, 而在呼叫的地方需要通過反射, 並且在lua側需要解決函式過載的問題,
上面的例子直接做了覆蓋. 就無法正常訪問非泛型方法函式了.
二. 既然泛型方法可以退化為非泛型, 那麼可以直接檢測有沒有同名的且同引數的非泛型函式, 如果沒有就把泛型方法的非泛型版新增到註冊函式中即可.
總結本文標題: c#泛型方法在lua中表示的一種設計詳解
本文位址: /ruanjian/csharp/254093.html
C 呼叫lua函式的一種通用辦法
在c 中呼叫lua函式的一般方式如下 如果有很多個指令碼函式需要呼叫,按上面的方式就要寫很多個與之對應的c 函式,當然也可以利用c 的過載,把函式名作為引數,每種引數組合實現乙個過載函式,則上面的 可以改為 如果有兩個不同的lua函式,它們的引數是一樣的,則可以共用同乙個common call,但是...
一種在C 中定義預設常量的方法
在c和c 裡我們可以用 define來定義巨集,比如下面這樣 define lac128 define lac256 ifdef lac128 define gf order 8 更多變數 endif ifdef lac256 define gf order 9 更多變數 endif 這個例子中我們...
lua對模組介面擴充套件的一種方法
lua中模組的實現,對於使用者來說就是乙個庫,引用此庫後,可以呼叫庫中實現的任意函式。使用庫,可以將一類功能相關的介面做封裝,並提供開放介面。參考 我們實現引用程式,往往要引用若干已經實現的庫檔案,這些庫大都是開源的,以此來加快應用開發程序 應用庫後,應用編碼中,會引用庫的一些api,例如會是 lf...