Gambas对象模型
1. 对象和类
Gambas中的一个
对象 是一个提供属性、变量、 方法和事件的数据结构。
通过引用访问对象。也就是说,通过一个指向对象的指针来访问对象。也就是说,用一个值为内存中对象数据结构地址的变量来访问对象。
可以用
PRINT语句来查看对象的地址:
Dim aStr As New String[]
Print aStr
类用来描述对象数据结构。
1.1. 类
所以每一个Gambas对象都有一个
类 来描述对象的所有公共属性、方法和事件。
该类也是一个Gambas对象,它的类是用
Class命名的类。
静态类 的所有成员均为
静态 (请看下面内容)。在Gambas中,静态类也被称为
模块 。
静态类不能被实例化:会创建一个没有动态变量的无用对象。.
虚类 是不能明确操作的隐含伪类。
1.2. 属性、方法和变量
属性和方法可以操作数据结构。
属性、方法、变量可以是
静态的 :
-
静态变量将被同一个类的所有实例共享。
-
静态属性或者方法仅能修改静态变量。
方法或者变量可以是
公共的 或
私有的 ,属性则总是公共的。
私有的标识符仅能在类的内部使用。
公共的标识符可以用在任何地方,需要通过指向该对象的引用来使用。
1.3. 引用
Gambas中没有内存回收器。所以每个对象都有一个
引用计数器 ,每当通过任何变量、数组、集合或其他对象引用该对象时该计数器加1。当被释放时,该计数器减1。
在创建对象时该引用计数器为0,并且在引用释放后其再次为0时,对象被释放。
1.4. 无效对象
一个对象会变成
无效 。例如因为该对象连接到被销毁的不受Gambas管理的内部对象。
试图使用无效对象会发生错误。
1.5. 特殊方法
特殊方法是在类中声明的以下划线作方法名开头的方法,并且解释器在下列情况下调用它们:
-
创建对象时。
-
释放对象时。
-
加载对象类时。
-
卸载对象类时。
-
将对象用作数组时。
-
枚举对象时。
-
将对象用作函数时。
-
试图使用未知的对象方法或属性。
-
要知道未知符号是方法还是属性。
-
当对象构造完成并准备使用时。
-
将对象序列化为流时。
-
从流中反序列化对象时。
更多信息请看
/wiki/api/cat/special。
2. 事件和事件伺服器
2.1.事件
事件是当对象有某些事情发生时发送的信号。
如果对象发生事件时,对象将一个引用指向其
事件伺服器 或者
父类 。
事件伺服器是另一个实现
事件处理 的对象。
事件处理 仅仅是每次事件发生时被调用的公共方法。
作为缺省,事件伺服器是被声明的新实例化对象中的当前对象。
对于发生的事件,对象必须有一个
事件名 。当使用
NEW语句和
AS关键字时,事件名被指派为对象实例名,而且该实例名也是所有事件处理方法的前缀。
Example
创建能发生事件的
Button控件。
Dim hButton As Button
hButton = New Button(Me) As "ButtonEventName"
Public Sub ButtonEventName_Click()
Print "This is the 'Click' event handler for the previous button."
End
如果没有指定事件名,对象将不会发生事件。
事件处理程序可以使用
STOP EVENT指令取消某些事件。
取消的效果取决于事件。它通常至少可以阻止可能的事件传播。
2.2.默认父对象 (or default default observer)
如果不使用
As "xxx"
的语法来定义指定事件名称,则某些对象可以具有默认父对象。
目前,只有
forms 具有默认父对象,它们将自己用作默认父对象。
这样做是为了模仿 Visual Basic™ 的行为;帮助老VB开发人员。
如果您创建一个窗体并使用
As "xxx"
语法指定其事件名称,则默认行为将被覆盖。
2.3. 锁定对象
对象可以被锁定,以便停止发生事件,也能被解锁,以便重新发生事件。
请看
Object.Lock和
Object.Unlock方法。
一些事件可以被事件处理程序通过使用
STOP EVENT语句取消。取消的结果取决于事件。
对象在其构建函数执行期间被自动锁定:不能发送和接收任何事件。
2.4. 事件伺服器
事件伺服器是允许拦截由其他对象引发的事件的对象,事件伺服器在“监视”事件。
可以在事件将发生或刚发生时进行拦截。
对于每一个被拦截的事件,事件伺服器将引发具有相同名称和参数的事件。
通过在事件伺服器事件处理程序内部使用
STOP EVENT,可以取消原来的事件。
Example
Private $hButton As Button
Private $hObserver As Observer
Public Sub Form_Open()
$hButton = New Button(Me) As "Button"
$hButton.Text = "&Click"
$hButton.Height = 28
$hButton.Width = 80
$hObserver = New Observer($hButton) As "Observer"
End
Public Sub Observer_Click()
Debug "The button has been clicked. I have cancelled the event!"
Stop Event
End
Public Sub Button_Click()
Debug "If you see me this is a bug!"
End
3. 继承
继承是一种方式,可以将一个类变成另一个类的特殊版本。
3.1. 什么是继承?
类继承其父类的每一个方法、属性、常数和事件。
3.2. 哪些类可以有父类?
可以继承任一个类,甚至一个本地类!
例如,可以创建一个继承于
ListBox,但是允许给每个列表条目关联一个标记的自定义MyListBox类。
注意:不能在窗体类文件中使用
INHERITS,因为窗体已经继承于
Form类。
继承树的深度不能大于16,这是Gambas解释器中的硬代码常数。
3.3. 虚拟调度
当调用或访问一个来自对象引用的方法或属性时,Gambas总是使用*虚拟调度*。
这意味着总是使用对象的真正的类,而且引用对象的变量无类型——就像在Gambas 1.0中一样。
3.4. 继承和构建函数
与所有我知道的对象语言相反,位于继承层级结构中的每一个类都要消耗被传递给构建函数的参数。
让我们假设有下面的继承树:
MyListBox ➞ ListBox ➞ Control
-
Control._new()不存在。
-
ListBox._new()带一个参数:父控件。
-
MyListBox._new()带一个参数:名称——这仅仅是个示例。
所以
NEW MyListBox
会带两个参数。
-
第一个会被发送给MyListBox._new()。
-
第二个给ListBox._new()。
小心:
ListBox._new()将被首先调用,所以当在MyListBox._new()中时,确保
ListBox控件存在。
所以参数必须按倒序指定。
然后可以用下面方法创建一个MyListBox控件:
hMyListBox = NEW MyListBox(hContainer, "Name")
在Gambas3中,构建函数参数排列有改变:
-
首先使用强制参数,然后使用可选参数(如果可用)。
-
顺序不再颠倒:年长的类的参数在先!
例如,有下列继承:
且MyForm构建函数是:
Sub _new(FirstArg As String, SecondArg as Integer, Optional ThirdArg As Integer)
Note: the
Form constructor takes no argument, and the
Window constructor takes an
optional parent argument.
最终的用法标记:
New MyForm(FirstArg As String, SecondArg As Integer, Optional Parent As Control, Optional ThirdArg As Integer)
更普遍的情况中,三层继承树的参数次序是:
-
Mandatory arguments of the grand-parent constructor.
-
Mandatory arguments of the parent constructor.
-
Mandatory arguments of the final constructor.
-
Optional arguments of the grand-parent constructor.
-
Optional arguments of the parent constructor.
-
Optional arguments of the final constructor.
3.5. 覆写标识符
当标识符被覆写时,子类中标识符的类型必须与父类中标识符的类型兼容。
规则是:
-
动态标识符必须用动态标识符覆写,静态的用静态的覆写。
-
方法必须用方法覆写。
-
属性必须用属性覆写。
-
只读属性可以用可读写属性覆写。
-
常数可以用常数或属性覆写。
-
变量不能被继承。
Since 3.12
此外,子类中的符号签名必须与父类中的字符签名
兼容 。
-
对于属性和常量,这意味着子数据类型必须与父数据类型兼容。
-
对于方法,这意味着子类返回数据类型和参数数据类型必须与父类返回数据类型以及参数数据类型兼容。
在以下任一情况下,子数据类型与其父数据类型兼容:
-
它们完全一样。
-
父数据类型是 Object ,子数据类型是任何类。
-
父数据类型是一个类,子数据类型是继承父类的另一个类。
4. 组件
Gambas
组件是用C、C++或者Gambas语言写成的外部共享库,这些组件向Gambas解释器中添加新的函数和类。
类根据所在的组件分组。
4.1. 缺省内部组件
解释器包含一个名为
gb的内部组件,该组件定义语言中的所有标准类。
该组件总是被默认加载,并且被作为是语言的一部分。
4.2. 标识符表
每个组件都有自己的私有类标识符表,所以类名称不会冲突。
4.3. 公用标识符表
一个公用标识符表存储有组件输出的所有类和当前工程输出的所有类,所以组件可以在一起工作。
如果在该公用标识符表中有名称冲突,最后加载的类通过继承覆写先前加载的同名类。换句话说,覆写的类扩展被覆写的类。
该最新特征能用于:
4.4. 工程标识符表
类似于某个组件,工程有自己的私有标识符,并且可以用
EXPORT关键字将其任意一些类输出到公用标识符表。
工程类的加载晚于所有组件。所以工程的输出类可以覆写在任何组件中声明的输出类。
4.5. 命名空间
Since 3.17
命名空间允许在导出类时选择类的名称。
在该公共名称中,现在可以插入冒号,其中冒号之前的部分是“命名空间”。
对于 Gambas 解释器来说,命名空间只是一种约定。它只处理类的完整导出名称。
该功能允许创建一个库,而不必担心与其他库可能发生的类名冲突。有关更多详细信息,请参阅
EXPORT 的文档。
参见