AHKv2类规范1.0
目录
(二)AHKv2-12-mono标准:自定义元函数规范及功能介绍
类作为面向对象编程中的重要一环,在各种编程语言中,都有很多规范和用法说明,下面我就AHK的类模块专门写出一则规范,目的是便于统一现行各类代码的使用逻辑,并对于新人书写AHK库提供指导。
一、类的基础内容
(一)类名,私有域与公有域
1、类名
通常上讲,我们定义一个类,就是通过(Class+类名)来实现的,例如:
- classa
- {
- }
就是定义了一个a这个类,其中a为类名,通常可以用如下方式获取:
- b := a()
- msgbox b.__class
- msgbox type(b)
2、私有域与公有域
我们学会创建类之后需要思考一个问题,什么样的数据是允许共享的,什么样的数据是只允许自己拥有的,我们来看下面这个例子:
- classpig
- {
- static value := 100
- name := "lily"
- }
对于公有域和私有域均分为属性和函数两部分,先讲结论,上述二者均属于属性,其中value属于公有属性,name属于私有属性,从二者的调用中就可以看出差距:
- a := pig()
- msgbox pig.value
- ; output => 100
- msgbox a.name
- ; output => "lily"
对于公有属性来说,调用时需要(类名.属性)来访问;而对于私有属性来说,调用时则是需要(类实例.属性)进行访问。
定义公有域的办法,就是在定义时,加上static这个静态前缀,用来标识这是公有域的属性或函数。
(二)元函数介绍与使用
在AHK中,每个类都内置了几个元函数(在python中称为魔术函数),下面我逐一进行介绍:
1、__Init函数
在类实例的创建过程中会涉及到两个函数,一是__Init函数,另一个是__New函数。我们先来介绍__Init函数。
Init顾名思义就是进行初始化,我们来看一下例子:
- classpig
- {
- __Init()
- {
- this.name := 100
- }
- }
- b := pig()
- msgbox b.name
我们会发现,尽管在类中我们并没有定义name这个私有属性,但是在创建b这个实例的过程中,我们自动进行了初始化,使得b这个实例获得了name属性。
*难点:this的理解。简单记忆就是,对于私有域,this指代的是实例本身;而对于公有域,this指代的是类本身。
2、__New函数
那么初始化之后,如何创建类实例呢?通过上述几个例子,你可以观察到,我们通过形如(类实例:=类名())来创建一个类实例。
那么如果我们需要创建一个独特的类实例应该怎么做?就是传参,那就需要用到__New这个函数。我们再看以下例子:
- classpig
- {
- __New(name)
- {
- this.name := name
- }
- }
- lily := pig("lily")
- msgbox lily.name
在这里我们就创建了一个独特的pig实例lily,它具有name属性并且与我们输入的参数相关。
3、__Get、__Set、__Call函数
以上几个函数都是与私有属性或私有函数相关,就放在一起讲了。
首先来看__Get函数,我们来看以下例子:
- classpig
- {
- __Get(name, params)
- {
- msgbox name
- msgbox params.length
- return "unknown"
- }
- }
- a := pig()
- msgbox a.name
我们可以发现,当我们访问a的name属性时,会调用__Get函数,他需要两个参数,第一个是属性的名称,第二个是传入的参数。Return的值就是我们访问后获取的值。
下面就第二个参数展开详述,继续看一个例子:
- classpig
- {
- __Get(name, params)
- {
- msgbox params.length
- return "unknown"
- }
- }
- a := pig()
- msgbox a.name[2]
我们会发现这时候获得的params.length值为1,经过更为详细的分析就能知道此时的params为[2],代表着我们传入了一个参数2。
__Set函数与__Get函数是一对元函数,理解了__Get函数的运行机理就不难理解__Set函数,下面看一个例子:
- classpig
- {
- __Set(name, params, value)
- {
- msgbox value
- }
- }
- a := pig()
- name := "lily"
我们会发现__Set函数的传入参数相较__Get函数多了一个value,即为所要赋的值。
__Call函数与上述二者差距在于,__Call函数面向的是私有函数,其余使用与__Get基本保持一致。来看一个例子:
- classpig
- {
- __Call(name, params)
- {
- msgbox name
- msgbox params.length
- return "unknown"
- }
- }
- a := pig()
- msgbox a.name(2)
简单来说,你可以把__Call理解为把__Get调用时的[]变成()。
*注意:这里建议不要轻易自己创建__Get、__Set、__Call函数,如果需要建立后面有相关帮助可以查看,可能会遇到死循环等问题。
4、__Item函数
这个函数对于数据类型类有重要意义,例如Array和Map类。我们来看一个例子:
- classpig
- {
- name := array()
- __New(name)
- {
- this.name.push(name*)
- }
- __Item[index]
- {
- Get => this.name[index]
- Set => this.name[index] := value
- }
- }
- a := pig(["lily", "lisa"])
- msgbox a[2]
- a[2] := "nana"
- msgbox a[2]
我们可以发现,__Item的作用就是让类实例拥有和Array类似的使用方式。其中Get和Set意义类似__Get函数和__Set函数。
5、__Enum函数
这个算是比较麻烦的一个元函数,它的机理比较复杂。先来个示例体验一下:
- classpig
- {
- name := array()
- __New(name)
- {
- this.name.push(name*)
- }
- __Enum(_)
- {
- this.index := 1
- return fn
- fn(&value)
- {
- if this.index > this.name.length
- {
- this.index := 1
- return false
- }
- value := this.name[this.index++]
- return true
- }
- }
- }
- a := pig(["lily", "lisa"])
- fori in a
- msgbox i
我们一点点分析__Enum的各项参数,首先是他输入的参数,就是例子中的“_“,他指代的是迭代返回数目,例如for i in a迭代数目就是1,而for i, j in a迭代数目为2。再看return fn,这里fn是一个函数,需要接收引用用以返回迭代值,返回为true或false,当fn返回false时停止迭代。
我们仔细剖析“接收引用用以返回迭代值“这句话的含义,我们可以这么理解,在for i in a中,我们临时命令一个未知变量为i,它作为一个容器,从__Enum函数中取得值并在for循环中可以使用(超出for循环范围后失去作用)。引用的数目根据迭代的数目自行设计,在AHK的设计理念中,迭代最多数目不得超过19。
6、__Delete函数
如何判断一个类实例是否删除呢?这个就要介绍现在最流行的引用计数法。请看下面这两个例子:
- classpig
- {
- name := array()
- __New(name)
- {
- this.name.push(name*)
- }
- __Delete()
- {
- msgbox 1
- }
- }
- a := pig(["lily", "lisa"])
- a := 1
- classpig
- {
- name := array()
- __New(name)
- {
- this.name.push(name*)
- }
- __Delete()
- {
- msgbox 1
- }
- }
- a := pig(["lily", "lisa"])
- b := a
- a := 1
上述两个例子哪一个实例被删除了?当然是第一个,在第二个中,b还保留着实例的信息,相当于实例仍被引用,就不会触发垃圾回收机制,也就不会将实例删除。
__Delete函数的触发条件就是当一个实例的所有引用结束时,触发这个函数,这个函数没有输入参数。
7、ToString函数
这个函数是AHKv2beta后续版本的更新内容,主要用于String强制转换类型的使用以及msgbox的使用,以及其他需要String的情况。
下面简单给一个实例,这个函数本身并不困难:
- classpig
- {
- name := array()
- __New(name)
- {
- this.name.push(name*)
- }
- ToString()
- {
- return "type: pig"
- }
- }
- a := pig(["lily", "lisa"])
- msgbox(a)
我们可以得到返回值为"type: pig"。
*注意:在AHKv2beta后续版本中,如果一个类没有ToString函数,使用msgbox的时候将不会返回空值,而会直接报错。
(三)继承
简单讲讲类的继承,其实原则非常简单,就是将被继承类的一切公有域和私有域复制到继承类上去,关键字为extends。例如:
- classlist extends array
- {
- }
新的类list继承了array的一切属性,比如push、pop等等。
二、关于库类的定义建议
(一)自定义元函数以及自定义函数的配合使用
我认为,对于AHK的库而言,他所写的类一定要包含一些共同之处,比如我想写一个新的数据类型list,那我想要知道list里有多少元素,可以自定义一个__Len函数,再在全局范围内,写一个len函数用于调用类的__Len以便捷获取长度,如下例:
- classlist extends array
- {
- __Len()
- {
- return this.length
- }
- }
- len(obj)
- {
- return__len()
- }
- a := list(1, 2, 3)
- msgbox len(a)
(二)AHKv2-13-mono标准:自定义元函数规范及功能介绍
AHKv2-13-mono标准 | ||
函数名 | 功能 | 备注 |
__cmp | 规定该类的比较度量 | |
__def | 规定该类函数的标准模板 | |
__future | 在不考虑兼容性的情况下添加的函数 | |
__hash | 规定该类的哈希值计算 | |
__len | 规定该类的容量度量 | |
__rely | 规定该类的前置库 | |
__str | 规定该类参与运算时的字符串形式表达 | |
static __version | 规定该类的版本号 |
评论(0)