lua 类支持属性不能被修改
背景
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
__indexmetamethod: 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
__newindexmetamethod does for table updates what__indexdoes for table accesses. When you assign a value to an absent index in a table, the interpreter looks for a__newindexmetamethod: 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 类支持属性不能被修改的更多相关文章
- python添加、修改、删除、访问类对象属性的2种方法
1.直接添加.修改.删除.访问类对象属性 class Employee (object): empCount = 0 def __init__(self, name, salary) : self.n ...
- Selectivizr-让IE6~8支持CSS3伪类和属性选择器
一.下载和demo 下载: 您可以狠狠地点击这里:selectivizr-1.0.0.zip(右键-[目标|链接另存为] 9.24K) demo: 您可以狠狠地点击这里:Selectivizr部分属性 ...
- Python小白学习之如何添加类属性和类方法,修改类私有属性
如何添加类属性和类方法,修改类私有属性 2018-10-26 11:42:24 类属性.定义类方法.类实例化.属性初始化.self参数.类的私有变量的个人学习笔记 直接上实例: class play ...
- Python 类中__init__()方法中的形参与如何修改类中属性的值
一.__init__()方法 如果__init__()方法为 class Cat(): def __init__(self,num) : self.num=num Python中类的__init__( ...
- XML之自动生成类,添加,修改,删除类的属性
1. class ClassHelperDemo { public static void Main() { #region 演示一:动态生成类. //生成一个类t. Type t = ClassHe ...
- ibernate学习笔记5---实体类或属性名与数据库关键字冲突、hql命名参数、hql实现通用分页
一.实体类或属性名与数据库关键字冲突问题1.实体类名与数据库中的关键字冲突比如:实体表User与oracle中的系统表冲突解决方式1:在xml中添加table属性,指定表名,使其不与name默认相等 ...
- 解决和排查 "必须使用适当的属性和方法修改 User-Agent" 错误时遇到的一些坑
解决 必须使用适当的属性和方法修改 User-Agent 错误 问题描述:近在项目中有一个需求为需要在 Http 的Header里面添加一个User-Agent参数,当请求时.项目本身的目标框架是 . ...
- guxh的python笔记六:类的属性
1,私有属性 class Foo: def __init__(self, x): self.x = x 类的属性在实例化之后是可以更改的: f = Foo(1) print(f.x) # 1 f.x ...
- c#自定义ORM框架---(泛型&反射&实体类扩展属性<附带通用增、删、查、改>)
该教材主要是运用到泛型.反射和实体类扩展属性 步骤一.建立扩展属性类 实体类扩展属性要继承Attribute基类完成 [AttributeUsage(AttributeTargets.Property ...
随机推荐
- HTML5编码规范
为每个 HTML 页面的第一行添加标准模式(standard mode)的声明,这样能够确保在每个浏览器中拥有一致的展现. 用两个空格来代替制表符(tab) -- 这是唯一能保证在所有环境下获得一致展 ...
- YARN-MR 大数据第二天
大数据第二天 1.YARN(管理和调度集群中的各类资源) 1.1 产生的背景: 1.直接源于MR版本1的缺陷(如:单点故障.难以支持MR之外的计算框架等) 2.多计算框架各自为站,数据共享困难(如:M ...
- Linux学习笔记(6)-文件I/O
持续一个礼拜的出差终于结束了,本次出差真是收益良多,不仅品尝了正宗的大闸蟹,同时也是第一次体验了产品的现场实施流程. 明天开始继续学习Linux! ----------------------分割线- ...
- Unity Standard Assets 简介之 Cameras
这篇介绍Cameras资源包. 文件夹下有名为“CameraGuidelines.txt”的说明文档,简要介绍了各个预设的作用和用法. Prefabs文件夹: CctvCamera.prefab: 视 ...
- [NOIP2016]换教室 D1 T3 Floyed+期望DP
[NOIP2016]换教室 D1 T3 Description 对于刚上大学的牛牛来说, 他面临的第一个问题是如何根据实际情况中情合适的课程. 在可以选择的课程中,有2n节课程安排在n个时间段上.在第 ...
- js-倒计时自动隐藏
<!doctype html><html><head><meta charset="utf-8"><title>无标题文 ...
- 使用TouchScript做2D按钮实现长按功能
导入TouchScript 下载地址:https://www.assetstore.unity3d.com/#/content/7394 把TouchScript和Touch Debugger两个预设 ...
- Twentydaysgone
这个暑假,在学校呆了一个月,考了两次科目二,被驾校坑了五百块,四级又挂了,段位差点掉到白银 参加二十多天工作室的培训,学长直接带着做itheima的某某安全卫士,跟着视频也迷迷瞪瞪敲完了代码,一知半解 ...
- Windows 7 常用快捷键
常用的快捷键 Win键+D 回到桌面/当前界面(切换)Win键+M 回到桌面Win键+E 资源管理器Win键+R 运行 Win键+U 控制面板->所有控制面板项->轻松访问中心 Win键+ ...
- PHP (sendmail / PHPMailer / ezcMailComposer)发送邮件
一. 使用 PHP 内置的 mail() 函数 1. Windows 下 环境:WampServer2.5(Windows 10,Apache 2.4.9,MySQL 5.6.17,PHP 5.5.1 ...