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:=theFuncObjtheLen:=“I Love You”.length运行的结果就是 theLen=10。但是这个方法的缺点也很明显,要通过Func()获取对象,非常不方便,那你想定义多个方法的时候是无法实现的,因为 字段 就那么一个,被覆盖就没有了。extends 方案还有一个方案就是利用 extends,虽然我们不可以改动 base,但是我们可以改 base 的 base ,也就是利用 extends。[注5]”".base.base:=StrBasetheLen:=“I Love You”.lengthDeBugDeepPrintln(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,得到许可并标明出处和原链接后方可转载。未经授权,禁止转载。版权所有 ©心如止水 保留一切权利。文章版本:v1AHK版本:1.1.30.00