類型內省 |
|
首先,我們為何需要類型內省?您甚至可能完全不需要使用類型內省。鴨子型別 [2] 和 [多型性] 可以在沒有程式設計師介入的情況下根據類型進行分派。您可能仍需要檢查類型的原因,例如為了除錯目的 [3] 或類型檢查 (LuaTypeChecking)。您可能還想要以特定自訂方式分派程式碼,例如 [string.gsub] 的行為會根據 repl
是字串、表格或函式而有所不同。不幸的是,界線有時會變得模糊,例如 FuncTables 是具有呼叫運算子、讓它們表現得像函式的表格。另外,您可能會想要定義一個函式,從字串或由檔名指定的檔案載入資料,但這兩個都是字串,因此您需要其他方法來區分它們(例如兩個獨立的函式,如 [loadstring] 和 [loadfile])。
[type] 函式會傳回值的類型作為字串。Lua 中只有八種基本類型("nil"
、"number"
、"string"
、"boolean"
、"table"
、"function"
、"thread"
或 "userdata"
)。
C API 中的 [lua_type] 函式類似於 type
,但會傳回整數常數。它也會區分兩種 userdata:LUA_TUSERDATA 和 LUA_TLIGHTUSERDATA(請參閱 LightUserData)。還有一個 lua_typename
函式,用於將該整數轉換回 type
傳回的型式字串。
[io.type] 函式用於確定指定的物件是否是檔案處理常式(以及是否開啟或關閉)。許多使用者函式庫遵循類似的模式,提供自己的函式來檢查指定的物件是否為函式庫定義的類型,傳回 true/false
或類別名稱字串。此函式使用下方所述的方法來實作。但是,這種風格可能會違反鴨子型別並可能禁止(LuaVirtualization)。
相等運算子,更精確地說是 [rawequal] 函數可以檢查兩個值是否擁有相同的恆等性(並且據此,它們表示相同的物件,因為物件擁有獨特的恆等性)。如果類別中的所有物件都儲存為資料表中的鍵,那麼你可以透過對資料表建立索引的方式來檢查恆等性(最好使用弱鍵資料表,儘管在 Lua 5.2 之前有一項警告--GarbageCollectingWeakTables)
local isfoo_ = setmetatable({}, {__mode='k'}) function newfoo() local self = {} isfoo_[self] = true return self end function isfoo(o) return isfoo_[o] end
如果類別中的所有物件,而且只有這些物件,擁有特定的元資料表(或由 __metatable
元方法傳回的值),那麼你可以檢查那個元資料表
local foo_mt = {} function newfoo() local self = setmetatable({}, foo_mt) return self end function isfoo(o) return getmetatable(o) == foo_mt end
為了取得使用者自訂類型的名稱作為字串,你可能首先會取得物件的類型,然後從類型取得名稱。你可能會對類型執行 tostring
以此方式來取得名稱,但是有些人認為 tostring
只適合除錯。你也可以在類型中,甚或在物件本身中儲存一個 _NAME
欄位 [9][4] (TypeOf)。選擇這個欄位名稱的原因是,模組的 _NAME
欄位是由 [module] 函數設定的,而且模組通常用作類型,而且,即使沒有使用模組函數(LuaModuleFunctionCritiqued)的人,仍有可能會遵循使用 _NAME
慣例。不幸的是,對物件建立索引可能會引發錯誤,而不是傳回 nil
(鴨子類型也可能會有這個問題)。
有些人已修改 type
函數以支援使用者自訂類型,可能是透過查詢 __type
元方法來支援 [5][6]。這麼做可能會中斷只期望傳回基本類型或在全域新增一些負載的現有程式碼。不過,你可以在區域內交換成自訂的 type
函數:local type = mytype
。其他人則讓使用者自訂類型(或子類型)只作為 type
的第二個傳回值傳回 [7][8]。
有些物件導向架構(ObjectOrientedProgramming)實作了自己的內省能力,但是這些能力可能與不是由那個架構建立的物件不相容。
並非所有這些類型內省方法都能完全防範蓄意的濫用(請參閱 SandBoxes)。不過,在正常的 Lua 使用情況下,無法偽造物件的恆等性。此外,透過限制對除錯函式的存取並提供 __metatable
元方法,你就能防止公眾存取物件的元資料表,以及公眾得知元資料表的恆等性。這些機制能夠防止不受信任的程式碼濫用。
注意:對於允許你 print
不同物件而不讓 Lua 崩潰的相對簡單的函數,請參閱 IntrospectionFunctionsLua。