C 泛型方法在lua中表示的一種設計詳解

2022-09-26 03:06:09 字數 3593 閱讀 8833

前言

在進行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...