共计 3584 个字符,预计需要花费 9 分钟才能阅读完成。
AB012-[中级] AHK 元编程实战:用__Call()魔改默认基对象
在上一篇文章中,介绍了 AHK 元编程的基本概念和常用元函数__Get(),其实__Set()和它是大同小异,这里就不单独说了。下面主要是来说一下 ” 默认基对象 ”。
默认基对象
什么是默认基对象?
AHK 中有两种数据类型,分别是 String 和 Object,String 可以是直接量。
但其实 String 的本质也是 Object,只不过他也是一个系统内置的 Object 罢了。
更改 String 的实际需求
由于 AHK 中 String 并没有内置方法,所以很多时候会非常的不方便。
比如当我们想获取 String 长度的时候,我们需要 StrLen(Str),其实最符合直觉的办法应该是 Str.Length。
当然还有很多我们需要的方法没有提供,比如最基本的 Str.toArray(转为字符数组),Str.CharAt(提取某一位置的特定字符)等等等。
那现在我们就来给 String 加入这些实用的方法,下面将会以最简单的 Str.Length 为例子演示。
魔改
获取默认基对象
String 的基对象可以由任何字符串获取,这个是非常好理解的,String 是一定是 StringClass 对象,所以可以这么获取 ””.base。
要注意的是,base 本身不能被替换,因为如果被替换,那么 String 的语法就全都完蛋了。[注 1]
方案 1:替换 base 成员
base 里面的元函数成员是可以被我们替换的,我们想实现 Str.Length 这种形式,需要对__Get()进行替换,那么我们该怎么实现这个替换呢?[注 2]
想一下上一节讲到的 ” 对象协议 ”,()这个语法对应的协议是什么?
很明显就是__Call(),那么什么东西含有__Call()?
只要能被 () 语法所调用的都可以是含有__Call()的。
回顾一下之前学过的内容,大概有这么几种可能,首先是 Func 对象,然后是 ObjBindMethod 对象,还有 MethodObject。[注 3]
其实 ObjBindMethod 我们并不需要,因为我们现在可以自定义,ObjBindMethod 只是在我们不能获取 Object 的时候使用的,现在能获取,显然就用不到;另外我们需要预留出参数使用,我们并不知道现在的参数是多少,所以更不能进行 Bind。
Func 对象替换
这里提一句,这个办法官方叫做 ” 伪属性 ”,” 属性 ” 之前我们提到过,其实 ” 属性 ” 可以看做__Get()和__Set()的语法糖。
这样思路就非常简单了,我们先定义一个函数,之后使用 Func(FuncName)获取一下。
但是这里要特别注意的是,我们定义函数的时候,需要给 对象 留出一个参数。
我们先来看一下之前的例子。
MateObj:=new Mate()
DeBugDeepPrintln(MateObj[1,2],”MateObj[1,2] >>> “)
Class Mate{
__Get(aName,Para*){
if (aName=1){
return “A”
}
}
}
你可以看到之前的例子,第一个参数是 aName,其实还有一个隐含的参数没有写出来,这个参数就是 Object,方法必须从属 Object,当其被 extends 使用的时候,这个 Object 就是 this,直接被使用的时候 Object 就是 ClassObject 本身。[注 4]
但是当我们直接使用 函数 的时候,并没有从属于 Object,所以我们必须手动把这个参数给加上去。
StringGet(Str){
StrLen(Str)
}
theFuncObj:=Func(“StrLen”)
“”.base.__Get:=theFuncObj
theLen:=”I Love You”.length
运行的结果就是 theLen=10。
但是这个方法的缺点也很明显,要通过 Func()获取对象,非常不方便,那你想定义多个方法的时候是无法实现的,因为 字段 就那么一个,被覆盖就没有了。
extends 方案
还有一个方案就是利用 extends,虽然我们不可以改动 base,但是我们可以改 base 的 base,也就是利用 extends。[注 5]
“”.base.base:=StrBase
theLen:=”I Love You”.length
DeBugDeepPrintln(theLen,”theLen >>> “)
class StrGetBase{
__Call(aStr,aName,aParams*){
if(ObjHasKey(this,aName)){
OutPut :=this[aName].Call(aStr)
return OutPut
}
else
return “”
}
}
class StrBase{
class __Get extends StrGetBase{
length(){
return StrLen(this)
}
;………….
;………….
}
}
这个方法的好处就是显而易见了,后面可以跟无穷无尽的方法,你加多少个方法都可以。
之后我会把自己用的比较多的 String 方法 上架 BeanLib,有兴趣可以下载源码看看实现。
关于隐藏的两个元函数
元函数除了这两个之外还有两个,不知道为什么在帮助文档上没有提,这里简单的说一下。
一个是__Init,还有一个是 _NewEnum,在帮助文件上,几乎找不到它们的踪迹(只有两处略微提到一句)。
第一个的意思是 ”initialize”,翻译为 ” 初始化 ”,也就是在 new 之前调用的。
第二个就比较容易理解,就是 for 语法的元函数,调用枚举器。
我就不多说了,有兴趣查英文资料吧,中文没有。当然如果你会 C ++,直接看源码也行。
本人英语渣,正在努力,看你们的啦,我啃不动。发现了啥好东西,别藏着掖着啊,做人要厚道。
当然还有__New 和__Delete,不过好像想不出什么用处来,帮助文件上,其实也没有提到他们作为 ” 元函数 ” 的用例。
最后提一嘴__Set()
想半天觉得这个__Set()还是有个地方需要说一下的,__Set()的设计需要遵循 return value,也就是参数 value = return 值。
为什么有这个要求呢?
是因为预制的对象都是这么搞的,if(b=v1:=v2)这种用法很常见,如果要搞特例的话,别人用起来是很蛋疼的。
元编程还有什么用?
在第一篇文章中,我提到过,” 我不知道有啥用 ”,其实我已经用这个方法改 String 很久了,连我自己都忘了,今天写第二篇文章才想起来。
至于其他的用法,应该会有很多吧,去英文网站上看了一下发现了这个精辟的帖子。
里面货很多,元函数的用法,只有你想不到,没有他做不到。
有空的话你可以搬出点东西来自己用,然后顺手整理一下,投稿到 BeanLib,非常欢迎。
注释
[注 1]:帮助文件中并没有解释,base 不能被直接替换的原因,但是我认为这个解释是显而易见的。
[注 2 /3]:在 AHK 中,Class 内的方法是可以直接用 Call() 调用的,而且他们是占据字段 (Field) 的。
比如,如果 Func 是你定义的一个函数,你可以再给 Func 赋值,比如 Func:=”ABC”,Func 函数是不会被覆盖的;但是 Func 如果是 方法,那么就会被直接给覆盖,这个和 Class 是一个原理。
官方没有给他们起一个名字,显而易见他们叫做 MethodObject(方法对象)是非常合适的,因为 官方把同样占据 字段 的 Class 叫做 Class objects(类对象)。
[注 4] Class/Method 本身就是对象,Method 就不存在是否 static 的问题,都可以调用。
[注 5] extends 翻译成 ” 继承 ” 比较多,实际上它的原意是 ” 扩展 ”/” 扩增 ”。B extends A,意思就是 ”B 从 A 延伸而来 ”,换一种说法就是 ”B 是 A 的基础 ”,所以 B.Base:=A,和 A extends B 是一个意思。
End
心如止水是 Java/AHK 持续学习者,欢迎您来和我探讨 Java/AHK 问题 ^_^
GitHub
欢迎您来访问我的 GitHub,在这里您可以看到我的代码分享,关注我的最新动态。
欢迎给 新生的 BeanLib 投稿,每一位贡献者的名字都将被铭记。
更多文章:
[专栏] AHK 程序设计 – 简书(优先持续更新)
[基础] [GIF 动图] 绕过中文输入法发送文本的 3 种方法
[基础] AHK 函数对象系列 - 绑定函数对象 v3
[基础] AHK 函数对象系列 - 绑定方法对象
[基础] 在 AHK 中实现函数重载的效果
[基础] AHK 函数对象系列 - 对象属性与数据域保护 v2
[中级] AHK 元编程初步: 对象协议和__Get()
问题解答:
[问题解答] 示例不能运行吗? – 关于 AHK 程序设计系列文章示例问题的解释
版权声明:
该文章版权系“心如止水”所有,欢迎分享、转发,但如需转载,请联系 QQ:3404624865,得到许可并标明出处和原链接后方可转载。未经授权,禁止转载。版权所有 ©心如止水 保留一切权利。
文章版本:
v1
AHK 版本:1.1.30.00