VB.Net中文教程(6) 母子对象关系
2024-07-10 13:01:17
供稿:网友
1. 特殊whole-part关系
---- 母子对象关系
大家已经熟悉父子类别关系﹐也就是「继承」关系了。于此说明另一种常见关系── 母子对象。一般称之为「组合/部分」关系。日常生活中﹐处处可见到这种母子对象。例如﹐客厅内有沙发、桌子、椅子等等。客厅是母对象﹐沙发、桌子、椅子是子对象。再如计算机屏幕上的窗口内有按钮、选择表、对话盒等等。窗口是母对象﹐按钮、选择表是子对象。于此将说明如何建立母子对象关系。有了关系﹐母子就能互相沟通了。母子对象之间﹐如何沟通呢﹖也就是说﹐母对象如何呼叫子对象的程序呢﹖反过来﹐子对象如何呼叫母对象的程序呢﹖欲呼叫对方的程序﹐必先与对方建立关系才行。因之﹐如何建立母子对象关系﹐是顶重要之课题﹗
请先看个例子﹐有两个类别──room和desk。若room代表房间﹐desk代表房间内的桌子﹐则它们会诞生母子对象﹕
通常﹐您会依房间的大小来决定桌子的大小。因之﹐desk对象应跟room对象沟通﹐取得房间的大小﹐才决定自己的大小。若有个room之参考﹐则desk对象就能跟room对象沟通了。于是﹐可设计下述vb程序:
'ex01.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
protected rsize as double
shared motherobject as room
public sub new()
motherobject = me
end sub
shared function getmother() as room
getmother = motherobject
end function
public function getsize() as double
getsize = rsize
end function
end class
class desk
protected dsize as double
public sub new()
dsize = room.getmother().getsize() * 0.18
end sub
public function getsize() as double
getsize = dsize
end function
end class
'----------------------------------------------------
class myroom
inherits room
private rd as desk
public sub new()
rsize = 100
rd = new desk()
end sub
public sub show()
messagebox.show("room size: " + str(rsize))
messagebox.show("desk size: " + str(rd.getsize()))
end sub
end class
'----------------------------------------------------
public class form1
inherits system.winforms.form
public sub new()
mybase.new()
form1 = me
'this call is required by the win form designer.
initializecomponent()
'todo: add any initialization after the initializecomponent() call
end sub
'form overrides dispose to clean up the component list.
public overrides sub dispose()
mybase.dispose()
components.dispose()
end sub
#region " windows form designer generated code "
.....
#end region
protected sub form1_click( byval sender as object,
byval e as system.eventargs)
dim r as new myroom()
r.show()
end sub
end class
此程序输出:
room size: 100
desk size: 18
desk类别内之指令──
room.getmother().getsize()
就是desk对象与room对象之沟通了。myroom类别有个rd参考﹐指向子对象﹐如下﹕
母对象经由rd参考来跟子对象沟通。反过来﹐子对象经由room.getmother()参考跟母对象沟通。
2. 母子对象谁先诞生﹖
在vb里﹐有时候子对象比母对象早诞生完成。就像建造房子﹐建到半途﹐改而建造桌子﹐桌子建好了﹐再继续把房子建完成。由于这不太合乎人们的日常生活习惯﹐令人感到困惑﹗在计算机软件上﹐也有类似的冲突。例如﹐在windows 下﹐母窗口建好了﹐才建立窗内的按钮、选择表等子对象。但vb有时并非如此﹗因之﹐如何化解这冲突﹐是个重要的话题。兹拿上小节的例子来说明吧﹗该例子中﹐其诞生对象之过程为﹕
step 1: 先诞生母对象。
step 2: 接着建立子对象。
这过程合乎人们的习惯﹐是美好的。其原因是﹕myroom类别在其建构者new()程序里呼叫desk的new()诞生desk子对象。假若您写下述程序﹐就出问题了﹗
'ex02.bas
'some error here!
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
protected rsize as double
shared motherobject as room
public sub new()
motherobject = me
end sub
shared function getmother() as room
getmother = motherobject
end function
public function getsize() as double
getsize = rsize
end function
end class
class desk
protected dsize as double
public sub new()
dsize = room.getmother().getsize() * 0.18
end sub
public function getsize() as double
getsize = dsize
end function
end class
'----------------------------------------------------
class myroom
inherits room
private rd as new desk()
public sub new()
rsize = 100
end sub
public sub show()
messagebox.show("room size: " + str(rsize))
messagebox.show("desk size: " + str(rd.getsize()))
end sub
end class
'----------------------------------------------------
public class form1
inherits system.winforms.form
public sub new()
mybase.new()
form1 = me
'this call is required by the win form designer.
initializecomponent()
'todo: add any initialization after the initializecomponent() call
end sub
'form overrides dispose to clean up the component list.
public overrides sub dispose()
mybase.dispose()
components.dispose()
end sub
#region " windows form designer generated code "
.....
#end region
protected sub form1_click( byval sender as object,
byval e as system.eventargs)
dim r as new myroom()
r.show()
end sub
end class
其对象之建造过程为﹕
step 1: 指令──
dim r as new myroom()
呼叫myroom()建构者new()。它再呼叫room()建构者new()将room部分建好。
step 2: 执行指令──
private rd as new desk()
呼叫desk建构者new()﹐建好desk子对象。此时会执行指令──
dsize = room.getmother().getsize() * 0.18
step 3: myroom建构者new() 继续将母对象建完成。
此时会执行指令── rsize=100
其中,getsize()程序会取出rsize 值﹐但那时还未执行rsize=100 指令﹐那来
的rsize 值呢﹖所以出问题了。此程序可能输出如下错误结果﹕
room size: 100
desk size: 0
如何解决上述问题呢﹖常见方法是﹕
把会出问题的指令﹐从建构者程序中提出来﹐放到另一程序里。
例如下述程序:
'ex03.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
protected rsize as double
shared motherobject as room
shared function getmother() as room
getmother = motherobject
end function
public overridable sub create()
motherobject = me
end sub
public function getsize() as double
getsize = rsize
end function
end class
class desk
protected dsize as double
public sub create()
dsize = room.getmother().getsize() * 0.18
end sub
public function getsize() as double
getsize = dsize
end function
end class
'----------------------------------------------------
class myroom
inherits room
private rd as new desk()
public sub new()
me.create()
end sub
public overrides sub create()
mybase.create()
rsize = 100
rd.create()
end sub
public sub show()
messagebox.show("room size: " + str(rsize))
messagebox.show("desk size: " + str(rd.getsize()))
end sub
end class
'----------------------------------------------------
public class form1
inherits system.winforms.form
public sub new()
mybase.new()
form1 = me
'this call is required by the win form designer.
initializecomponent()
'todo: add any initialization after the initializecomponent() call
end sub
'form overrides dispose to clean up the component list.
public overrides sub dispose()
mybase.dispose()
components.dispose()
end sub
#region " windows form designer generated code "
.......
#end region
protected sub form1_click(byval sender as object, byval e as system.eventargs)
dim r as new myroom()
r.show()
end sub
end class
此程序输出:
room size: 100
desk size: 18
像指令── dsize = room.getmother().getsize() * 0.18﹐已挪到新设的create()程序里。待母对象完全建好了﹐才会呼叫这create()程序﹐getsize() 就能取得正确值了。myroom的new()呼叫create()程序时﹐母子对象皆已建造完成了。create()内部依人们的习惯来设定对象之值,例如建立母子对象之关系。
如此就不会出问题了。
new()与create()分离之后,myroom类别里的指令:
class myroom
inherits room
private rd as new desk()
public sub new()
me.create()
end sub
........
也能写为:
class myroom
inherits room
private rd as desk
public sub new()
rd = new desk()
me.create()
end sub
........
只要确保desk类别的指令──
dsize = room.getmother().getsize() * 0.18
是在myroom类别的指令──
rsize = 100
之后执行即可了。
上述的子对象是透过shared 程序来取得母对象的参考值﹐然后才跟母对象沟通。如果不透过shared程序,也可以采取下述方法:
'ex04.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
protected rsize as double
public overridable sub create()
end sub
public function getsize() as double
getsize = rsize
end function
end class
class desk
protected dsize as double
protected mymother as room
public sub create(byval mo as room)
mymother = mo
dsize = mymother.getsize() * 0.18
end sub
public function getsize() as double
getsize = dsize
end function
end class
'----------------------------------------------------
class myroom
inherits room
private rd as desk
public sub new()
rd = new desk()
me.create()
end sub
public overrides sub create()
rsize = 100
rd.create(me)
end sub
public sub show()
messagebox.show("room size: " + str(rsize))
messagebox.show("desk size: " + str(rd.getsize()))
end sub
end class
'----------------------------------------------------
public class form1
inherits system.winforms.form
public sub new()
mybase.new()
form1 = me
'this call is required by the win form designer.
initializecomponent()
'todo: add any initialization after the initializecomponent() call
end sub
'form overrides dispose to clean up the component list.
public overrides sub dispose()
mybase.dispose()
components.dispose()
end sub
#region " windows form designer generated code "
......
#end region
protected sub form1_click(byval sender as object,
byval e as system.eventargs)
dim r as new myroom()
r.show()
end sub
end class
此程序输出:
room size: 100
desk size: 18
desk之对象含个参考 mymother ﹐指向其母对象。这项关系是在母子对象皆建好时﹐才由create()程序所建立的。于是﹐建立出母子对象之关系﹕
综上所述,当myroom类别使用如下指令──
private rd as new desk()
时,才必须把new()与create()分离。如果使用如下指令──
private rd as desk
public sub new()
rd = new desk()
.....
end sub
就不必分离了,原因是:new()与create()的执行顺序是一致的,例如两者可合并如下的vb程序:
'ex05.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
protected rsize as double
public function getsize() as double
getsize = rsize
end function
end class
class desk
protected dsize as double
protected mymother as room
public sub new(byval mo as room)
mymother = mo
dsize = mymother.getsize() * 0.18
end sub
public function getsize() as double
getsize = dsize
end function
end class
'----------------------------------------------------
class myroom
inherits room
private rd as desk
public sub new()
rsize = 100
rd = new desk(me)
end sub
public sub show()
messagebox.show("room size: " + str(rsize))
messagebox.show("desk size: " + str(rd.getsize()))
end sub
end class
'----------------------------------------------------
public class form1
inherits system.winforms.form
public sub new()
mybase.new()
form1 = me
'this call is required by the win form designer.
initializecomponent()
'todo: add any initialization after the initializecomponent() call
end sub
'form overrides dispose to clean up the component list.
public overrides sub dispose()
mybase.dispose()
components.dispose()
end sub
#region " windows form designer generated code "
......
#end region
protected sub form1_click(byval sender as object,
byval e as system.eventargs)
dim r as new myroom()
r.show()
end sub
end class
此程序输出:
room size: 100
desk size: 18
3. 特类别继承与母子对象
您已很熟悉父子类别关系了﹐这继承关系的另一面是母子对象关系。例如﹐
'ex06.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
private mother as myroom
public sub new(byval mo as myroom)
mother = mo
end sub
public function getid() as string
getid = mother.yourid()
end function
end class
class myroom
inherits room
public sub new()
mybase.new(me)
end sub
public function yourid() as string
yourid = "vip888"
end function
end class
'----------------------------------------------------
public class form1
inherits system.winforms.form
public sub new()
mybase.new()
form1 = me
'this call is required by the win form designer.
initializecomponent()
'todo: add any initialization after the initializecomponent() call
end sub
'form overrides dispose to clean up the component list.
public overrides sub dispose()
mybase.dispose()
components.dispose()
end sub
#region " windows form designer generated code "
......
#end region
protected sub form1_click(byval sender as object, byval e as system.eventargs)
dim r as new myroom()
messagebox.show(r.getid())
end sub
end class
此程序输出﹕
vip888
myroom()建构者new()将me值给 room的mother参考。于是mother参考到母对象r。room类别是myroom之父类别﹐但room之对象却是myroom之子对象。
如果将上述room的指令:
public function getid() as string
getid = mother.yourid()
end function
更改为:
public function getid() as string
getid = me.yourid() 'error!!
end function
就错了。因为room类别里没有定义yourid()程序。事实上,vb的继承机制里已经有母对象的参考值了,vb的overridable机制就是基于它而呼叫到子类别(母对象)的程序的。例如上述程序相当于:
'ex07.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
public function getid() as string
getid = me.yourid()
end function
public overridable function yourid() as string
end function
end class
class myroom
inherits room
public sub new()
mybase.new()
end sub
public overrides function yourid() as string
yourid = "vip888"
end function
end class
'----------------------------------------------------
public class form1
inherits system.winforms.form
public sub new()
mybase.new()
form1 = me
'this call is required by the win form designer.
initializecomponent()
'todo: add any initialization after the initializecomponent() call
end sub
'form overrides dispose to clean up the component list.
public overrides sub dispose()
mybase.dispose()
components.dispose()
end sub
#region " windows form designer generated code "
.......
#end region
protected sub form1_click(byval sender as object, byval e as system.eventargs)
dim r as new myroom()
messagebox.show(r.getid())
end sub
end class
此程序输出﹕
vip888
上述ex07.bas比ex06.bas好的地方是:
ex06.bas程序的room类别里面用到myroom的名称。而ex07.bas程序的room类别里面并没用到myroom的名称,因此room类别可以先设计,myroom类别能后来才设计,这是继承机制的目的之一。不过,如果您一定不想用继承与overridable概念的话,可使用vb的interface机制来改善ex06.bas程序,如下:
'ex08.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
interface imyroom
function yourid() as string
end interface
class room
private mother as imyroom
public sub new(byval mo as imyroom)
mother = mo
end sub
public function getid() as string
getid = "roomid: " + mother.yourid()
end function
end class
class myroom
implements imyroom
private base as room
public sub new()
base = new room(me)
end sub
public function yourid() as string implements imyroom.yourid
yourid = "vip888"
end function
public function getid() as string
getid = base.getid()
end function
end class
'----------------------------------------------------
public class form1
inherits system.winforms.form
public sub new()
mybase.new()
form1 = me
'this call is required by the win form designer.
initializecomponent()
'todo: add any initialization after the initializecomponent() call
end sub
'form overrides dispose to clean up the component list.
public overrides sub dispose()
mybase.dispose()
components.dispose()
end sub
#region " windows form designer generated code "
.......
#end region
protected sub form1_click(byval sender as object, byval e as system.eventargs)
dim r as new myroom()
messagebox.show(r.getid())
end sub
end class
此程序输出﹕
roomid: vip888
一般使用委托(delegation)来代替继承时,常用的手法。然而上述ex08.bas程序的myroom类别里面用到了room名称,如果您不希望如此,可定义一个iroom接口,供myroom类别使用,如下程序:
'ex09.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
interface imyroom
function yourid() as string
end interface
interface iroom
function getid() as string
sub connect(byval m as imyroom)
end interface
class room
implements iroom
private motherobject as imyroom
public function getid() as string implements iroom.getid
getid = motherobject.yourid() + " ***"
end function
public sub connect(byval m as imyroom) implements iroom.connect
motherobject = m
end sub
end class
class myroom
implements imyroom
private base as iroom
public sub connect(byval r as iroom)
base = r
r.connect(me)
end sub
public function yourid() as string implements imyroom.yourid
yourid = "dog888"
end function
public function getid() as string
getid = base.getid()
end function
end class
'----------------------------------------------------
public class form1
inherits system.winforms.form
public sub new()
mybase.new()
form1 = me
'this call is required by the win form designer.
initializecomponent()
'todo: add any initialization after the initializecomponent() call
end sub
'form overrides dispose to clean up the component list.
public overrides sub dispose()
mybase.dispose()
components.dispose()
end sub
#region " windows form designer generated code "
.......
#end region
protected sub form1_click(byval sender as object, byval e as system.eventargs)
dim my as new myroom()
dim base as new room()
my.connect(base)
messagebox.show(my.getid())
end sub
end class
此程序输出﹕
roomid: vip888
room类别里面没用到myroom名称,而且myroom类别里没有用到room名,因此两个类别可独立设计。这是分布式组件软件,如mts(microsoft transaction server)等系统里的常用手法。n