关于如何在vb.net中重载类变量、数组

FlyingLeaf886 2006-03-15 12:31:37
在vb.net中重载变量

重载函数是很正常的事,但有的时候,需要重载变量,如下例

class Person
public Name as string
end class

class Orgnization
public head as Person

end class

Orgnization是一个类,它代表一个组织,它有一个head成员,代表这个组织的领导,属Person类型。
现在我想从Orgnization派生一个类School,并使用它已经定义功能性函数,但我对Person类型的head不满意,因为它的字段太少了,只有一个Name,我想加一个Age字段,那么我该怎么办?
在C++中,这是很简单的事,只要把Orgnization设计成模板类就行了,但在VB中,这是个大难题——因为我实际上需要重载head变量。
在vb.net中引入了代理Delegate,以及隐藏Shadows概念,如果把它们和重载函数结合起来,可以实现对变量的重载,思路是这样的:
1、 Delegate使得函数可以被当成参数传递给另一个函数;
2、 函数是有返回值的,而且可以被重载,如果我让一个变量总是被一个函数指向,然后把这个函数重载,就可以通过重载函数,变相地重载这个变量,但有一个前提,即这个需要被重载的变量必须声明为Object类型或者其它的基类类型(如果能够确定重载类型总是这个基类类型的派生类型);
3、 通过Shadows把需要被重载的基类变量隐藏起来,然后在派生类中定义一个同名的派生变量(类型是被重载的变量的类型的派生类型);
4、 在基类中,凡是要引用被重载的变量,都不直接去引用,而是引用一个指向它的Delegate类型函数;
5、 在派生类中,把这个Delegate类型的函数重载,从而变相实现对基类变量的重载。
例:

Namespace p
Public Class Person
Public Name As String
End Class

Public Class Orgnization '基类
Public head As New Person 'head需要被重载
Protected Delegate Function F_HEAD() As Person

Protected Overridable Function GetHead() As Person '关键:这个函数总是指向基类的head,
且可被重载
Return head
End Function

Public sub DisplayHead()
MsgBox(GetHead().Name.ToString()) ‘间接调用head变量
这里有个必须注意的地方:GetHead后必须带括号
如果不想这么别扭,也很简单——把GetHead设计成
属性
End sub
'…
End Class

Public Class SchoolHead 'head将被重载成这个类型
Inherits Person
Public Age As Integer
End Class

Public Class School
Inherits Orgnization
Public Shadows head As New SchoolHead '先用Shadows隐藏基类的head,然后声明一个新的head

Protected Overrides Function GetHead() As Person '重载这个函数,因而基类所有对head
的调用,原来是被映射到基类的GetHead
函数,现在则被映射到派生类的这个
函数。而这个函数是指向派生类的
head变量的,从而基类所有函数对原
来的head的调用全被重定向到派生
类的head上
Return head
End Function
'…
End Class

现在做段代码来测试一下:
public sub t
Dim p As New School
head.Name = "Lj"
head.Age = 100
DisplayHead()
end sub
结果显示的是lj,这说明DisplayHead最终作用到了派生类的head上——Shadows的语义说明中指出,基类的函数总作用在基类的被隐藏成员上,但这里用了一个小技巧,实现了重定向,使基类的函数作用到了派生类的成员上。
从派生类的角度来看,重载head没有费太多代码,只是加了一个重载函数而已,关键是基类要设计好——应了那句老话:前人栽树,后人乘凉

这个例子说明了重载变量最基本的思路,但它也有明显的不足:
1、 派生类的GetHead()为Person类型,而不是SchoolHead类型,因而在派生类的函数中每次调用这个函数,还必须得用CType把它转换过来,很不方便。但如果认为这个派生类的head变量不会再被继承,就可以不用使用GetHead函数,而是直接用派生类的head就行了。但是如果认为这个派生类的head有可能还要被重载,那就没有办法,必须要转换。对于使用频繁的情况,当然还可以再设计一个私有函数(或属性),用它来包装CType,并返回SchoolHead类型,而在其它派生类的函数中,用这个私有函数再转一道,这可以让写代码轻松点;
2、 如果基类有一个公共属性Head(当然也可能是函数),它对外传递head对象,这就有点麻烦,因为派生类需要它对外传递SchoolHead类型的实例。这也有办法来解决:再用一个Shadows把基类的这个公共属性隐藏起来,并设计一个同名的Head属性,在Get段,可以这么写:
return head
如果基类中还有其它有用的代码,则可以在前面加几句:head=MyBase.Head …
3、 这个方法虽然在功能上实现了重载变量,但每调用一个重载变量(不论是在类的内部,还是外部)都要转好几个函数,程序性能明显要受影响,因此如果不是特别需要,也没必要这么干,不如直接粘代码。
4、 序列化问题。上面这个例子中,如果对School进行序列化,会发现在声明序列化器时出错,但是只要把基类Orgnization中的head成员声明为protected就可以解决问题。其实不光是这个应用,只要是用Shadows去隐藏基类变量,且这个基类变量是public 型,就会出现这个错误。我猜想原因是:虽然基类变量被隐藏了,但并非它就不存在了,事实上它还在内存里,但序列化器却没有发现这个问题,它只知道要对公共变量去进行检测,结果发现有两个同名的变量,所以就不知道怎么办了,只有报错。不过只要把这种变量声明为private或protected就行了——这也是一个缺点吧。
...全文
166 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
FlyingLeaf886 2006-03-15
  • 打赏
  • 举报
回复
更进一步:
如果要重载数组怎么办?
也没有问题,思路是一样的
现在把上面的例子部分修改一下:
Public Class Orgnization '
Public head(1) As Person 'head改为数组
Protected Delegate Function F_HEAD() As Person() ‘注:很奇怪,Person不带括号竟然
也可以运行通过!!

Protected Overridable ReadOnly Property GetHead() As Person() ' 返回值改成数组类型
Get
Return head
End Get
End Property

Public Function DisplayHead()
MsgBox(GetHead()(0).Name.ToString()) ‘这里又是个别扭的写法,改属性就好点
End Function

Sub New()
head(0) = New Person ‘别忘了创建实例
head(1) = New Person
End Sub
'…
End Class

下面改派生类:
Public Class School
Inherits Orgnization
Public Shadows head(1) As SchoolHead '同样声明为数组

Protected Overrides ReadOnly Property GetHead() As Person() ‘同上
Get
Return head
End Get
End Property
'…

Sub New()
head(0) = New SchoolHead ‘依然同上
head(1) = New SchoolHead
End Sub
End Class

测试代码:
sub t
Dim p As New School
head(0).Name = "Lj"
head(0).Age = 100
DisplayHead()
MsgBox("OK")
end sub

结果正确,显示”Lj”,说明DisplayHead还是调用的派生类的数组,而不是基类的。
programart_life 2006-03-15
  • 打赏
  • 举报
回复
受教了,多谢!

16,554

社区成员

发帖
与我相关
我的任务
社区描述
VB技术相关讨论,主要为经典vb,即VB6.0
社区管理员
  • VB.NET
  • 水哥阿乐
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧