Methods lua:检查方法的主题

Methods lua:检查方法的主题,methods,lua,subject,Methods,Lua,Subject,我创建了一个对象并指定了一个如上所述的方法。现在我这样称呼它: obj = {} function obj:setName(name) print("obj: ", self) print("name: ", obj) end 然后,自标识符引用obj。我的问题是,该函数也可能通过 obj:setName("blabla") 在这种情况下,obj将不会作为参数传递,“blabla”将取代self参数,而不是提供名称。这是因为函数声明中的:运算符只是 obj.setName(

我创建了一个对象并指定了一个如上所述的方法。现在我这样称呼它:

obj = {}

function obj:setName(name)
    print("obj: ", self)
    print("name: ", obj)
end
然后,自标识符引用obj。我的问题是,该函数也可能通过

obj:setName("blabla")
在这种情况下,obj将不会作为参数传递,“blabla”将取代self参数,而不是提供名称。这是因为函数声明中的:运算符只是

obj.setName("blabla")
我能否正确地检查self是否真的是主题/函数是否由冒号运行?它不能从argCount判断,也不能直接在函数中写入obj,因为它将被实例化,并且函数是从我定义它的范围之外引用的。我唯一的想法是检查self是否拥有一个成员“setName”

但那也不干净

编辑:现在就这样做:

function obj:setName(name)
    if ((type(self) ~= "table") or (self.setName == nil)) then
        print("no subject passed")

        return
    end

    print("obj: ", self)
    print("name: ", obj)
end

您可以将元表分配给对象,在setName方法中,只需检查self的元表是否合适:

local function checkMethodCaller()
    local caller = debug.getinfo(2)

    local selfVar, self = debug.getlocal(2, 1)

    assert(self[caller.name] == caller.func, [[try to call function ]]..caller.name..[[ with invalid subject, check for correct operator (use : instead of .)]])
end

function obj:setName(name)
    checkMethodCaller()

    print(self, name)
end
编辑:


当然,如果有人故意替换或删除您的元表,它将完全破坏该功能。

在脚本编写中,文档通常胜过类型检查。如果您检查所有内容,最终将看到一些性能影响。首先添加好的函数文档标题

也就是说,我想到了以下选项:

通常只需进行类型测试,自参数就足以防止键入错误,因为
name
通常是一个字符串,因此如果意外键入
obj.setName(“Foo”)
的类型是一个字符串而不是一个表

obj = {}
local objmt = {}

setmetatable(obj, objmt)

function obj:setName(name)
    if getmetatable(self) ~= objmt then
        error("Panic, wrong self!")  -- or handle it more gracefully if you wish
    end
    self.name = name
end
当然,您也可以使用参数的数量。请注意,我使用了
==2
而不是
==2
,这很有用,因为如果链接一些调用,例如
obj:setName(foo:getInfo())
,其他返回值将中断执行,即使
name
参数可能是正确的值

-- Test type of argument
function obj:setName(name)
   assert(type(self) == "table");
   -- more code
end
下面的变体更进一步,它不仅确保您的
self
是一个表并包含正确的函数,而且确保
self
是同一个表。这是因为比较表不会比较它们在Lua中的内容,而是比较它们唯一的表ID

然而,这也要求每个实例化的方法和对象都有一个闭包。这是一点开销

-- Check number of arguments
function obj.setName(...)
   assert(select('#', ...) >= 2);

   local self, name = ...;
   -- more code
end
我想到的最后一个变体(主要是因为我编写了一些非常类似的代码)是在使用原型时通过构造函数跟踪对象

-- Compare against object table
function Construct()
   local o = {};

   function o:setName(name)
      assert(self == o);
      -- more code
   end

   return o;
end

明显的好处是,您的方法不再是每个对象的单独闭包。但是,通过这种方式,您还可以轻松地执行其他操作,例如使对象不可变,或实现基本继承。

您可以在一行中编写它:
assert(self.setName)
以停止在错误调用时执行脚本。有了主意,请将self.setName与obj.setName(函数)进行比较。如果匹配,则必须调用该函数。是否确实要执行此操作?从长远来看,这种魔术可能会令人困惑,并且容易出错。也许您可以更改
-使用对象来使用
方法的定义,或者您可以添加一个标志来告诉您的处理函数是否应该使用
方法?@missingno这是一个短期运行。我已经编写了一个小库,它将为其他用户实现一个专用的目的。他们不应该以错误的方式意外访问该功能/我自己也会遇到这种情况,也不会选择错误的操作员。方法表示法的可读性更好。一个额外的标志更容易出错,而且有些函数具有可变参数。“它们不应该意外地以错误的方式访问功能”:但是,您如何才能确保“对象检测”启发式始终有效?你真的确定你不能改变你的界面,只有一种方法来做事情吗?
-- Compare against object table
function Construct()
   local o = {};

   function o:setName(name)
      assert(self == o);
      -- more code
   end

   return o;
end
local Objects = setmetatable({}, { __mode = "k" });
-- the __mode just makes the GC ignore this table so that your objects can be
-- collected by the GC if not used any more. It would work without the metatable
-- but be a memory leak.
local Prototype = {
   setName = function(self, name)
      assert(IsMyObject(self));
      -- more code
   end
}

function IsMyObject(self)
   return not not Objects[self];
end

function ConstructMyObject()
   local o = {};
   Objects[o] = { __index = Prototype };
   return setmetatable(o, Objects[o]);
end