Lua实现C#的Get/Set属性访问器

虽然Lua没有C#一样的属性,但是它的强大的元表和元方法能够实现很多功能,本篇博客就介绍如何用lua的元表和原方法实现C#中的get、set属性访问其功能。

class.lua

class.lua是实现lua的类,我的之前博客有些过简易版的,属性访问的的实现相当于在最简易版的基础上进行扩展,博客链接:Lua 实现C#中的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
local mt = {}

function class(clsName, base)
    local cls = {}
    base = base or mt
    cls.__get__ = {}
    cls.__set__ = {}
    setmetatable(cls, {__index = base})
    cls.clsName = clsName or "default"
    cls.base = base
    cls.new = function(...)
        local cls_instance = {}
        cls_instance.getset_values = {}
        for k,v in pairs(cls) do
            cls_instance[k] = v
        end
        local cls_instance_mt = {
            __index = function(t, k)
                if cls[k] then
                    return cls[k]
                end
                if t.__get__[k] then
                    t.__get__[k](t)
                    return t.getset_values[k]
                end
                if string.sub(k, 1, 2) == "__" then
                    local tmpK = string.sub(k, 3, -1)
                    if t.getset_values[tmpK] then
                        return t.getset_values[tmpK]
                    end
                end
            end,
            __newindex = function(t, k, v)
                if t.__set__[k] then
                    t.__set__[k](t, v)
                    cls_instance.getset_values[k] = v
                    return
                end
                if string.sub(k, 1, 2) == "__" then
                    local tmpK = string.sub(k, 3, -1)
                    if t.getset_values[tmpK] then
                        t.getset_values[tmpK] = v
                    end
                end
                rawset(t, k, v)
            end
        }
        setmetatable(cls_instance, cls_instance_mt)
        if cls_instance.onCreate then
            cls_instance:onCreate(...)
        end
        return cls_instance
    end
    return cls
end

重要扩展方式是给cls_instance添加了一个metatable,这个metatable功能有以下几方面:

  1. 检查xxx字段是否是__get____set__的内部字段(__get__.xxx
  2. 调用__get__ __set__ 对应的函数
  3. 修改getset_valuesxxx的值
  4. 判断__xxx(在实例中__xxx作为属性的真实存储值,如果不想通过get或者set获取,直接instance.__xxx就不会出发对应函数)
  5. _xxx转为xxxgetset_values中找值并返回

test.lua

分割线之上是声明一个类,分割线下是测试的代码,输出结果分别对应着p1.age = 10print(p1.__age)的每行输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
local Person = class("Person")
Person.onCreate = function(self, name)
    self.name = name
end
Person.__get__.age = function(self)
    print(self.name, " - 属性访问器:get age")
end
Person.__set__.age = function(self, value)
    print(self.name, " - 属性访问器:set age", value)
end
-----------------------------------
local p1 = Person.new("张三")
p1.age = 10
local tmpAge = p1.age
p1.age = p1.__age + 1
print(p1.__age)

--[[
    张三   - 属性访问器:set age    10
    张三   - 属性访问器:get age
    张三   - 属性访问器:set age    11
    11
]]