Lua 只讀 與rawget無視_newindex



1.沉默技能——拒絕__index和__newindex效果

雖然__index和__newindex是很好用的功能,但是,有時候我們又希望很純粹地去調用table或者給table賦值。

那,這時候怎麼辦?給table重新設置一個元表?不,這個做法很糟糕~

 

於是,體貼的Lua又給我們提供了這樣的調用方式,如下代碼:

  1.     local smartMan = {
  2.         name = "none",
  3.     }
  4.    
  5.     local t1 = {
  6.         hehe = 123;
  7.     };
  8.    
  9.     local mt = {
  10.         __index = smartMan,
  11.         __newindex = function(t, k, v)
  12.             print("別賦值!");
  13.         end
  14.     }
  15.    
  16.     setmetatable(t1, mt);
  17.    
  18.     print(rawget(t1, "name"));
  19.     print(rawget(t1, "hehe"));
  20.     rawset(t1, "name", "小偷");
  21.     print(t1.name);

通過rawget函數可以忽略元表的__index功效,純粹地從t1中調用字段。

rawget的第一個參數是要調用的table,第二個參數是table的字段名。

因此,通過rawget調用t1的name字段,只能返回nil,而調用hehe字段,則能正確取得值。

 

同樣的是,rawset函數可以忽略元表的__newindex功效,純粹地給t1賦值。

 

來看看輸出結果:

[LUA-print] nil
[LUA-print] 123
[LUA-print] 小偷

獲取name字段,輸出nil;

獲取hehe字段,輸出123;

修改name字段後,輸出”小偷”

 

這就相當於t1並不存在__index和__newindex元方法了。

怎麼樣,這個沉默技能很有意思吧。

 

2.只讀的table

吶,假設你又繼續是一個主程,你寫了一個很牛的功能,然後作爲主程的你,每晚都要回家看電影。

所以你的功能不得不交給公司裏那些剛畢業不到30年的新人去維護,讓他們天天加班到晚上6點半。(小若:喂!6點半算加班嗎?)

然而,這麼牛的功能,可不能被這些新人隨便改壞了,所以,除了保護table的元表之外,你還希望保護table的字段。

你要確保這些新人不會去修改你table的字段值。

沒錯,這時候就可以使用__index和__newindex來實現了,如下代碼:

  1. local function readOnly(t)
  2.     local newT = {};
  3.     local mt = {
  4.         __index = t,
  5.         __newindex = function()
  6.             error("別修改我!我是隻讀的!");
  7.         end
  8.     }
  9.     setmetatable(newT, mt);
  10.     return newT;
  11. end
  12. local days = readOnly({"星期一", "星期二", "星期日"});
  13.    
  14. days[2] = "星期三哪去了啊?" ;

這可能有點難弄懂,先來看看輸出結果吧:

[LUA-print] LUA ERROR: [string “src/main.lua”]:130: [string “src/main.lua”]:76: 別修改我!我是隻讀的!

沒錯,通過readOnly產生的table,是無法進行賦值操作的。

 

那麼,原理呢?我們來一步步思考吧:

a.首先,readOnly會創建一個新的table,然後把我們傳進去的table作爲__index元方法。

b.元表裏還增加了__newindex,用來阻止不存在字段的賦值操作。

c.readOnly返回的table已經不是我們原來的table了,它是一個空的table,但是它被設置了一個新的元表。

d.開始對days執行賦值操作:days[2] = “星期三哪去了啊?” 。

e.days是一個空的table,所以它不存在這個字段,也因此,會調用__newindex元方法,賦值失敗。

f.如果只是調用days,不進行賦值,如:print(days[2]); 則能正常輸出字段值,因爲days的元表裏有__index元方法。雖然days中不存在2這個字段,但是可以通過__index找到這個字段。

 

總而言之,最終,days成爲了一個只可以讀取,不能進行賦值操作的table。

發佈了12 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章