2013-05-19 45 views
1
obj = {} 

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

我創建一個對象並指定一個類似上面的方法。現在我這樣稱呼它:lua:檢查方法的主體

obj:setName("blabla") 

自身標識符指的是obj。我的問題是,該功能也可能被通過

obj.setName("blabla") 

訪問。在這種情況下,OBJ不會被作爲參數,「布拉布拉」將採取自我參數的地方,而不是服務的名稱傳遞。這是因爲:經營者在函數聲明僅僅是一個速記/糖語法

function obj.setName(self, name) 

我可以採用某種適當的檢查,如果真的自我是主體/如果該功能已經被結腸運行?它不能被argCount告知,也不能直接在函數中寫obj,因爲它會被實例化,並且函數被引用到我定義的範圍之外。我唯一的想法是檢查自己是否擁有一個成員「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 

但這也不乾淨。

編輯:現在這個樣子做它:

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 
+0

你可以把它寫在一行中:'assert(self.setName)'在錯誤的調用中停止執行你的腳本。 –

+0

有一個想法,比較self.setName obj.setName(函數)。如果這匹配,那必須是被調用的函數。 – user2399203

+0

你真的*確定你想要這樣做嗎?從長遠來看,這種魔法可能會令人困惑並容易出錯。也許你可以改變':'-using對象的定義來使用'.'方法,或者你可以添加一個標誌來告訴你的處理函數是否應該使用':'或'.'方法? – hugomg

回答

0

您可以將元表的對象和的setName方法中只檢查自我的元表是否合適:

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 

編輯:

當然,如果有人故意替換或刪除您的metatable,它將完全打破功能。

1

通常文檔勝過腳本中的類型檢查。如果你檢查一切,你最終會看到一些性能影響。首先添加好的功能文檔標題。

這就是說,下列選項浮現在腦海中:

通常只是型式檢驗自我的說法是足夠,以防止輸入錯誤,因爲name通常是一個字符串,所以如果你不小心輸入obj.setName("Foo")類型self是一個字符串,而不是一個表。

-- Test type of argument 
function obj:setName(name) 
    assert(type(self) == "table"); 
    -- more code 
end 

你當然也可以使用參數的數量。請注意,我使用的是>= 2而不是== 2,這很有用,因爲如果鏈接一些諸如obj:setName(foo:getInfo())之類的調用,否則即使您的name參數可能是正確的值,其他返回值也會中斷執行。

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

    local self, name = ...; 
    -- more code 
end 

下變形進入甚至更進了一步,它不僅可以確保你的self是一個表,包含正確的功能,但self非常同桌。這是有效的,因爲比較表不會比較它們在Lua中的內容,但它們的表格ID是唯一的。

但是,這也需要爲每個方法和對象實例化一個閉包。這是一個開銷。

-- 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 

明顯的最後一個變種好處是您的方法不再是針對每個對象的單獨關閉。但是,通過這種方式,您還可以輕鬆地完成其他任務,如使對象不可變或實現基本繼承。