美文网首页「河许人」AutohotkeyAHK程序设计AutoHotkey 之美
[中级] AHK元编程实战:用__Call()魔改默认基对象 v

[中级] AHK元编程实战:用__Call()魔改默认基对象 v

作者: d61f25068828 | 来源:发表于2019-01-08 19:42 被阅读2次

    _AB012-[中级] AHK元编程实战:用_Call()魔改默认基对象

    在上一篇文章中,介绍了AHK元编程的基本概念和常用元函数__Get(),其实__Set()和它是大同小异,这里就不单独说了。下面主要是来说一下"默认基对象"。

    默认基对象

    什么是默认基对象?

    AHK中有两种数据类型,分别是 StringObjectString 可以是直接量。

    但其实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,aName){
        if (aName="length")
            return StrLen(Str)
        else
            return
    }
    theFuncObj:=Func("StringGet")
    
    "".base.__Get:=theFuncObj
    
    theLen:="I Love You".length
    

    运行的结果就是 theLen=10

    但是这个方法的缺点也很明显,要通过Func()获取对象,非常不方便,那你想定义多个方法的时候是无法实现的[注6],因为 字段 就那么一个,被覆盖就没有了。

    方案2: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++,直接看源码也行。

    本人英语渣,正在努力,看你们的啦,我啃不动。发现了啥好东西,别藏着掖着啊,做人要厚道。

    SciTE自动完成列表中的元函数 当然还有__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是一个意思。
    [注6] 当然你可以通过检测aName的方法间接实现,但是方法内不能有方法,也就是说你所有的代码都要挤在一个方法里面,这个事情有多蛋疼想想就知道,我就不多说了。

    End

    心如止水是Java/AHK持续学习者,欢迎您来和我探讨Java/AHK问题 _

    GitHub

    欢迎您来访问我的GitHub,在这里您可以看到我的代码分享,关注我的最新动态。

    欢迎给 新生的 BeanLib 投稿,每一位贡献者的名字都将被铭记。

    更多文章:

    问题解答:

    [问题解答] 示例不能运行吗? - 关于AHK程序设计系列文章示例问题的解释

    版权声明:

    该文章版权系“心如止水”所有,欢迎分享、转发,但如需转载,请联系QQ:3404624865,得到许可并标明出处和原链接后方可转载。未经授权,禁止转载。版权所有 ©心如止水 保留一切权利。

    文章版本:

    v1
    v2:修复 Func 案例的错误(忘记检测aName,以及Func参数错误) 并且 增加注释6

    AHK版本:1.1.30.00

    心如止水

    相关文章

      网友评论

        本文标题:[中级] AHK元编程实战:用__Call()魔改默认基对象 v

        本文链接:https://www.haomeiwen.com/subject/irgbrqtx.html