背景

lua是类是借助表的来实现的, 类被定义后, 在使用场景下, 不希望被修改。如果被修改, 则影响的类的原始定义, 影响所有使用类的地方。

例如:

--- router.lua class file
router = class()
router.xxx = function xxx end --- app.lua
router.xxx = function yyy end

故提出新的要求:

1、 对于类在应用场景下,不能修改属性。

2、 对于类在应用场景下, 不能添加新的属性。

类的实现代码:

local _M = {}

-- Instantiates a class
local function _instantiate(class, ...)
-- 抽象类不能实例化
if rawget(class, "__abstract") then
error("asbtract class cannot be instantiated.")
end -- 单例模式,如果实例已经生成,则直接返回
if rawget(class, "__singleton") then
-- _G[class]值为本class的实例
if _G[class] then
return _G[class]
end
end local inst = setmetatable({__class=class}, {__index = class})
if inst.__init__ then
inst:__init__(...)
end --单例模式,如果实例未生成,则将实例记录到类中
if rawget(class, "__singleton") then
if not _G[class] then
_G[class] = inst -- 对类对象增加实例获取接口
class.getInstance = function ( self )
return _G[class]
end -- 销毁单例,为后续建立新单例准备
class.destroyInstance = function ( self )
_G[class] = nil
end
end
end return inst
end -- LUA类构造函数
function _M.class(base)
local metatable = {
__call = _instantiate,
__index = base
} -- __parent 属性缓存父类,便于子类索引父类方法
local _class = {__parent = base} -- 在class对象中记录 metatable ,以便重载 metatable.__index
_class.__metatable = metatable return setmetatable(_class, metatable)
end --- Test whether the given object is an instance of the given class.
-- @param object Object instance
-- @param class Class object to test against
-- @return Boolean indicating whether the object is an instance
-- @see class
-- @see clone
function _M.instanceof(object, class)
local meta = getmetatable(object)
while meta and meta.__index do
if meta.__index == class then
return true
end
meta = getmetatable(meta.__index)
end return false
end return _M

他山之石

http://www.lua.org/pil/13.4.5.html

    function readOnly (t)
local proxy = {}
local mt = { -- create metatable
__index = t,
__newindex = function (t,k,v)
error("attempt to update a read-only table", )
end
}
setmetatable(proxy, mt)
return proxy
end

应用

    days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"} print(days[]) --> Sunday
days[] = "Noday"
stdin:: attempt to update a read-only table

评价:

此方法虽然实现的 新需求两则, 但是不能够, 其借助有新增一级metatable, 破坏了现有的 原型链表继承结构。

__index & __newindex

local tab = {,  , aa="bb"}

setmetatable(tab, {__index={bb="cc"}, __newindex = function (t,k,v)
error("attempt to update a read-only table", )
end}) for k,v in pairs(tab) do
print(k, v)
end print(tab.bb) tab[] = tab["aa"] = tab["bb"] = tab["aabbb"] =

LOG

>lua -e "io.stdout:setvbuf 'no'" "luatest.lua"
1    1
2    6
aa    bb
cc
lua: luatest.lua:110: attempt to update a read-only table
stack traceback:
    [C]: in function 'error'
    luatest.lua:97: in function <luatest.lua:96>
    luatest.lua:110: in main chunk
    [C]: ?
>Exit code: 1

__index 释义

http://www.lua.org/pil/13.4.1.html

I said earlier that, when we access an absent field in a table, the result is nil. This is true, but it is not the whole truth. Actually, such access triggers the interpreter to look for an __index metamethod: If there is no such method, as usually happens, then the access results in nil; otherwise, the metamethod will provide the result.

    Window.mt.__index = function (table, key)
return Window.prototype[key]
end
    Window.mt.__index = Window.prototype

__newindex 释义

http://www.lua.org/pil/13.4.2.html

The __newindex metamethod does for table updates what __index does for table accesses. When you assign a value to an absent index in a table, the interpreter looks for a __newindex metamethod: If there is one, the interpreter calls it instead of making the assignment. Like __index, if the metamethod is a table, the interpreter does the assignment in that table, instead of in the original one.

这两个元表中的属性, 首先是以元方法的形式出现的, 然后做了易用性接口适配, 可以是表。

不引入metatable情况下,实现类属性只读

在类表中引入 __prototype 表, 专门用于存储 建立的 类field

__index 从__prototype中查找

__newindex 设置field到 __prototype 表中

oopclass

local _M = {}

-- Instantiates a class
local function _instantiate(class, ...)
-- 抽象类不能实例化
if rawget(class, "__abstract") then
error("asbtract class cannot be instantiated.")
end -- 单例模式,如果实例已经生成,则直接返回
if rawget(class, "__singleton") then
-- _G[class]值为本class的实例
if _G[class] then
return _G[class]
end
end local inst = setmetatable({__class=class}, {__index=class})
if inst.__init__ then
inst:__init__(...)
end --单例模式,如果实例未生成,则将实例记录到类中
if rawget(class, "__singleton") then
if not _G[class] then
_G[class] = inst -- 对类对象增加实例获取接口
class.getInstance = function ( self )
return _G[class]
end -- 销毁单例,为后续建立新单例准备
class.destroyInstance = function ( self )
_G[class] = nil
end
end
end return inst
end -- LUA类构造函数
function _M.class(base)
local metatable = {
__call = _instantiate,
} -- 先查原型表,然后查父亲类
metatable.__index=function(t, k)
local v = t.__prototype[k]
if v then
return v
end local parent = t.__parent
if parent then
return parent[k]
end return nil
end -- 缓存类的field
metatable.__newindex=function (t,k,v)
rawset(t.__prototype, k, v)
end local _class = {}
-- __parent 属性缓存父类
_class.__parent = base or {}
-- 存储此类的所有field
_class.__prototype = {} -- 在class对象中记录 metatable ,以便重载 metatable.__index
_class.__metatable = metatable -- 将类冷冻,不允许新建删除修改
_class.freeze = function ( self )
local mt = getmetatable(self) mt.__newindex=function (t,k,v)
error("class is frozen, cannot revise")
end
end return setmetatable(_class, metatable)
end --- Test whether the given object is an instance of the given class.
-- @param object Object instance
-- @param class Class object to test against
-- @return Boolean indicating whether the object is an instance
-- @see class
-- @see clone
function _M.instanceof(object, class)
local objClass = object.__class
if not objClass then
return false
end while objClass do
if objClass == class then
return true
end
objClass = objClass.__parent
end return false
end return _M

app

local oopclass = require("oopclass")

local class = oopclass.class
local instanceof = oopclass.instanceof local superTab = class()
superTab.test = function ( self )
print("superTab test")
end superTab:freeze() superTab.test2 = function ( self )
print("superTab test2")
end local tab = class(superTab) local tabObj = tab()
tabObj:test() print( instanceof(tabObj, tab) ) print( instanceof(tabObj, superTab) )

LOG:

>lua -e "io.stdout:setvbuf 'no'" "luatest.lua"
lua: .\oopclass.lua:85: class is frozen, cannot revise
stack traceback:
    [C]: in function 'error'
    .\oopclass.lua:85: in function <.\oopclass.lua:84>
    luatest.lua:17: in main chunk
    [C]: ?
>Exit code: 1

去掉freeze语句 superTab:freeze()

LOG:

>lua -e "io.stdout:setvbuf 'no'" "luatest.lua"
superTab test
true
true
>Exit code: 0

lua 类支持属性不能被修改的更多相关文章

  1. python添加、修改、删除、访问类对象属性的2种方法

    1.直接添加.修改.删除.访问类对象属性 class Employee (object): empCount = 0 def __init__(self, name, salary) : self.n ...

  2. Selectivizr-让IE6~8支持CSS3伪类和属性选择器

    一.下载和demo 下载: 您可以狠狠地点击这里:selectivizr-1.0.0.zip(右键-[目标|链接另存为] 9.24K) demo: 您可以狠狠地点击这里:Selectivizr部分属性 ...

  3. Python小白学习之如何添加类属性和类方法,修改类私有属性

    如何添加类属性和类方法,修改类私有属性 2018-10-26  11:42:24 类属性.定义类方法.类实例化.属性初始化.self参数.类的私有变量的个人学习笔记 直接上实例: class play ...

  4. Python 类中__init__()方法中的形参与如何修改类中属性的值

    一.__init__()方法 如果__init__()方法为 class Cat(): def __init__(self,num) : self.num=num Python中类的__init__( ...

  5. XML之自动生成类,添加,修改,删除类的属性

    1. class ClassHelperDemo { public static void Main() { #region 演示一:动态生成类. //生成一个类t. Type t = ClassHe ...

  6. ibernate学习笔记5---实体类或属性名与数据库关键字冲突、hql命名参数、hql实现通用分页

    一.实体类或属性名与数据库关键字冲突问题1.实体类名与数据库中的关键字冲突比如:实体表User与oracle中的系统表冲突解决方式1:在xml中添加table属性,指定表名,使其不与name默认相等 ...

  7. 解决和排查 "必须使用适当的属性和方法修改 User-Agent" 错误时遇到的一些坑

    解决 必须使用适当的属性和方法修改 User-Agent 错误 问题描述:近在项目中有一个需求为需要在 Http 的Header里面添加一个User-Agent参数,当请求时.项目本身的目标框架是 . ...

  8. guxh的python笔记六:类的属性

    1,私有属性 class Foo: def __init__(self, x): self.x = x 类的属性在实例化之后是可以更改的: f = Foo(1) print(f.x) # 1 f.x ...

  9. c#自定义ORM框架---(泛型&反射&实体类扩展属性<附带通用增、删、查、改>)

    该教材主要是运用到泛型.反射和实体类扩展属性 步骤一.建立扩展属性类 实体类扩展属性要继承Attribute基类完成 [AttributeUsage(AttributeTargets.Property ...

随机推荐

  1. HTML5编码规范

    为每个 HTML 页面的第一行添加标准模式(standard mode)的声明,这样能够确保在每个浏览器中拥有一致的展现. 用两个空格来代替制表符(tab) -- 这是唯一能保证在所有环境下获得一致展 ...

  2. YARN-MR 大数据第二天

    大数据第二天 1.YARN(管理和调度集群中的各类资源) 1.1 产生的背景: 1.直接源于MR版本1的缺陷(如:单点故障.难以支持MR之外的计算框架等) 2.多计算框架各自为站,数据共享困难(如:M ...

  3. Linux学习笔记(6)-文件I/O

    持续一个礼拜的出差终于结束了,本次出差真是收益良多,不仅品尝了正宗的大闸蟹,同时也是第一次体验了产品的现场实施流程. 明天开始继续学习Linux! ----------------------分割线- ...

  4. Unity Standard Assets 简介之 Cameras

    这篇介绍Cameras资源包. 文件夹下有名为“CameraGuidelines.txt”的说明文档,简要介绍了各个预设的作用和用法. Prefabs文件夹: CctvCamera.prefab: 视 ...

  5. [NOIP2016]换教室 D1 T3 Floyed+期望DP

    [NOIP2016]换教室 D1 T3 Description 对于刚上大学的牛牛来说, 他面临的第一个问题是如何根据实际情况中情合适的课程. 在可以选择的课程中,有2n节课程安排在n个时间段上.在第 ...

  6. js-倒计时自动隐藏

    <!doctype html><html><head><meta charset="utf-8"><title>无标题文 ...

  7. 使用TouchScript做2D按钮实现长按功能

    导入TouchScript 下载地址:https://www.assetstore.unity3d.com/#/content/7394 把TouchScript和Touch Debugger两个预设 ...

  8. Twentydaysgone

    这个暑假,在学校呆了一个月,考了两次科目二,被驾校坑了五百块,四级又挂了,段位差点掉到白银 参加二十多天工作室的培训,学长直接带着做itheima的某某安全卫士,跟着视频也迷迷瞪瞪敲完了代码,一知半解 ...

  9. Windows 7 常用快捷键

    常用的快捷键 Win键+D 回到桌面/当前界面(切换)Win键+M 回到桌面Win键+E 资源管理器Win键+R 运行 Win键+U 控制面板->所有控制面板项->轻松访问中心 Win键+ ...

  10. PHP (sendmail / PHPMailer / ezcMailComposer)发送邮件

    一. 使用 PHP 内置的 mail() 函数 1. Windows 下 环境:WampServer2.5(Windows 10,Apache 2.4.9,MySQL 5.6.17,PHP 5.5.1 ...