类型内省

lua-users home
wiki

类型内省 [1] 是指在运行时确定对象类型的能力。本页面介绍了在 Lua 中支持此功能的方法。

首先,为什么我们需要类型内省?你可能根本不需要使用类型内省。鸭子类型 [2][多态性] 可以根据类型进行调度,而无需程序员干预。你可能仍然需要检查类型,例如出于调试目的 [3] 或类型检查 (LuaTypeChecking)。你可能还想以特定自定义方式调度代码,例如 [string.gsub] 的行为方式取决于 repl 是字符串、表还是函数。不幸的是,界限有时会模糊,例如 FuncTables,它们是带有调用运算符的表,允许它们像函数一样工作。此外,你可能想定义一个函数,该函数从字符串或由文件名指定的 文件加载数据,但两者都是字符串,因此你需要其他方法来区分它们(例如,使用两个单独的函数,如 [loadstring][loadfile])。

[type] 函数以字符串形式返回值的基类型。Lua 中只有八种基本类型("nil""number""string""boolean""table""function""thread""userdata")。

C API 中的 [lua_type] 函数类似于 type,但返回一个整数常量。它还区分两种用户数据类型: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]

一些面向对象框架(面向对象编程)实现了它们自己的内省功能,但这些功能可能与不是用该框架创建的对象不兼容。

并非所有这些类型内省方法都能完全防止有意滥用(参见 沙箱)。然而,在正常的 Lua 使用中,对象的标识是无法伪造的。此外,通过限制对调试库的访问并提供 __metatable 元方法,可以防止公开访问对象的元表,以及公开元表标识。这些可以阻止来自不受信任代码的滥用。

--DavidManura

注意:对于一个相对简单的函数,它允许您在 Lua 崩溃的情况下打印不同的对象,请参见 IntrospectionFunctionsLua


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2013 年 4 月 6 日下午 4:51 GMT (差异)