搜档网
当前位置:搜档网 › VB编程语言入门学习

VB编程语言入门学习

VB编程语言入门学习
VB编程语言入门学习

https://www.sodocs.net/doc/592847774.html,之旅(一)—失踪的窗体

好久没有新作,架不住兄弟们的问询与诘问,只得拿些旧作来补上近来没空写作的缺乏。羞愧ing!这一

系列与Henry手记系列不一样,是一个人物办法的情形故事,开端发表于《计算机国际》,适用于https://www.sodocs.net/doc/592847774.html, 初

专家,老鸟们就不必在我这一系列的文章上浪费时间了。我期望自个有时间今后,能不断持续这一系列

的文章,参加许多的工程处理方案。一起,我的Henry手记也会持续写下去,以报答长时间以来撑持我的

一切兄弟。

布景:我叫Henry,刚从大学计算机专业结业,新参加索易公司的.NET开发小组;担任带我的喽罗是大李,.NET高手,是个诙谐又有耐性的家伙;老白是咱们有些的头头,常常带给咱们乖僻又难缠的需求。

榜首天上班,白司理就带着我去.NET技能小组签到。“大李,这是新来的同事,你要多帮帮他。”老白

指着我对一个眼镜后边透着闪闪目光的年轻人说。

“没疑问,他会做得极好的。”大李莫非会看相不成?仍是人家谦让呀?不论了,我只需自个尽力就对

了。

20分钟后,我现已在分配给我的一台P4电脑,WindowsXP平台下翻开了Visual https://www.sodocs.net/doc/592847774.html,的集成编译环

境(IDE)开端赏识了。一个多美丽的编译环境呀,能够自选运用时的个性是VB6的还是VC6的。我挑选的

是RAD功用好的VB6个性。

“你刚开端触摸.NET,在系统训练之前先加深点形象,看看https://www.sodocs.net/doc/592847774.html,与VB6有啥不一样吧。你创立两个窗体

,每个窗体上各有一个按钮,点击时躲藏其地址的那个窗体,切换出另一个窗体来。”大李的声响在耳

边响起,打断了我对IDE的赏识。但他提的需求也太简略了吧,在VB6中只需求两句写在按钮的单击呼应

中的句子就行了:

两个窗体:Form1与Form2。Form1上的按钮为: Command1,Form2上的按钮为: Command2 Private Sub Command1_Click()

Me.Hide

Form2.Show

End Sub

Private Sub Command2_Click()

Me.Hide

Form1.Show

End Sub所以,我毫不犹豫地在https://www.sodocs.net/doc/592847774.html,中内开端我的榜首个https://www.sodocs.net/doc/592847774.html,的项目了。首要,新建了一个https://www.sodocs.net/doc/592847774.html, 的Windows应用程序项目,主动生成了一个Windows Form:Form1,在其上参加一个按钮控件:Button1;然后,增加了一个Windows Form:Form2,在其上参加了一个按钮控件:Button2。

在描绘窗口双击Button1后,编译器会切换到代码窗口的Button1的单击呼应代码中。

在代码段中故计重施,当我想写Form2.Show时,在Form2之后打个点,呈现的智能感知菜单中,却没有

Show这一办法。真新鲜,这怎样能够?莫非https://www.sodocs.net/doc/592847774.html,改动有这么大。我昂首看了看站在我身旁的大李。大李马上面露浅笑,看来是正中他的骗局。他手指向屏幕,点了点Form1中的代码的最初处:

Public

Class Form1

Inherits了,如今的Form1是一个从

,调用的时分当然需求先实例化了,哈,也不难嘛。马上动

手:

Private Sub

Button1_Click(ByVal sender As System.Object,_

ByVal e As System.EventArgs)Handles Button1.Click

Me.Hide()

Dim frm2As New Form2()‘实例化Form2类,得到

一个目标

frm2.Show()

End Sub在Form2的Button2的单击工作中也相似地写了一段代码,运转一下,和VB6的效果差不多了。正

在得意中,大李走过来,在我的Form1中增加了一个文本框textbox1,清空其间的文字。然后运转了一下

程序。在运转中,他在文本框里写了几个字:“Henry的著作”,然后点击Button1切换到Form2,接着

点击Button2,从头呈现的Form1的文本框中却变成了空白。

“这……”我一时语塞,慢慢地想起点啥,“大概是程序经过New布局办法,每次点击按钮都会从头实

例化一次Form类吧?所以每次从头呈现的窗体都不是从前从前呈现过的那个。那从前的窗体也就失踪了

,调不出来了。”

“失踪?”大李不由笑了起来,“对呀,失踪在内存中。你每实例化一次,系统就会拓荒一块内存给一

个目标,并会有相应的线程对其进行操控与办理。若是作为进程中主线程的榜首个Form1被Hide起来,没有封闭它,那么即便你封闭后来新创立的一切窗体,进程也不会被间断。那么,用啥办法去找回你

失踪的窗体,或许说怎么就只运用一个目标,不再每次实例化呢?”

“大局目标!”我脱口而出,感受到大李模棱两可,只好硬着头皮持续说:“运用大局变量,使Form1

与Form2只实例化一次,这样就能够了。”

然后,在大李的注视下,我开端施行我的方案:

再创立一个模块文件:module1.vb,并且同时在:

处理方案办理器-〉项目名.sln -〉右点鼠标-〉特点-〉通用属性->惯例->发动目标-〉改为Module1

。让程序发动后先运转Module1中的代码指令,然后编写代码如下:

'Module1.vb

Module Module1

Public frm1As New Form1()‘界说Form1的共用变量实

Public frm2As New Form2()‘界说Form1的共用变量实例

Sub Main()

Application.Run(frm1)‘这句话标明程序发动了

modele后,会接下来运转frm1,即form1的一个实例。

主线程就变成运转期的frm1了。

End Sub

End Module这样一来,frm1、frm2就变成大局变量了,在Button1与Button2的单击呼应代码中只需写:

frm1.show就能够了。

再运转程序,可以了,“Henry的著作”一向都会保持在Form1中的文本框里。

大李老哥没等我欢笑起来就再次出手,在frm1躲藏起来的时分,封闭了Form2窗口(frm2实例),桌面上

啥窗体都没有了,进程又一次被堕入在无法间断的状况下。My God,面向目标的https://www.sodocs.net/doc/592847774.html,,爱你莫非真的这么难……

https://www.sodocs.net/doc/592847774.html,之旅(二)—布局与析构

在Form1窗体的实例被躲藏的时分,封闭了Form2窗体的实例,使我失去了对主线程的人工操控,进程无

法正常封闭了。只好运用Ctrl+Alt+Del调出系统进程办理器,强行间断了该进程。为了防止昂首看见大李的笑脸,我只好垂头想办法。

有了,我只需能截获Form2实例封闭的音讯,不就能够再调出躲藏的主线程窗体了吗?在Form2的基类事

件(Base Class Event)中重载Closing办法进行处置:

Private Sub Form2_Closing(ByVal sender As Object,

ByVal e As_

Handles MyBase.Closing

frm1.Show()

End Sub哈,很便利,我封闭了Form2窗体的实例后,被躲藏的那个frm1又呈现了。“嗯,”大李总算发话了,“你再点击一下Form1窗体上的Button1试试。”

我背心一凉,隐约感受大李等待着的即是这个时分。无法的我还只能照他说的去做。公然,弹出一个出错窗口.

“你封闭了frm2这个Form2的实例,也就完毕了这个目标的生存期,”大李看来是蓄势已久了,“这即是犯错提示中所说的‘无法拜访名为Form2的已处置目标’。当咱们封闭一个窗口的时分,会宣布一个终

止呼应,并将该窗口目标,就象上面界说的frm2,送入停止行列,公共言语运行库的废物收回器盯梢着这个目标的生存期,此刻就会调用此目标基类,比方Form2的Dispose办法,用于毁掉目标并收回资源。所以……”

“所以我只需判别一下frm2是不是被毁掉就行了,若是毁掉了,我就再布局一个实例不就行了?”我恍然大悟道。

大李首次浅笑地址了答应说:“用frm2.IsDisposed就能够来判别了。”

我心照不宣地写道:

Private Sub

Button1_Click(ByVal sender As System.Object,_

ByVal e As System.EventArgs)Handles Button1.Click

If frm2Is Nothing Or frm2.IsDisposed Then

‘判别目标是不是被毁掉

frm2=New Form2()

End If

Me.Hide()

frm2.Show()

End Sub这下完善多了,两个窗体之间的切换也不会有这么多别扭的疑问了。我转过身,看到大李现已找了把椅子坐在我的身边。

“你来说说,对https://www.sodocs.net/doc/592847774.html,的窗体实例的创立与毁掉的进程吧。”

我整理了一下杂乱的思路,长吁了一口气,开端说:“一个窗体类,比方Form1类是经过调用其基类,就是Form类的New办法来创立实例、Dispose办法来毁掉实例。”

“没错。”大李边说话,一边在我的程序中点击开来被代码窗口主动折叠起来的" Windows 窗体描绘器

生成的代码":

Public Sub New()

MyBase.New()

'该调用是Windows窗体描绘器所必需的。

InitializeComponent()

'在InitializeComponent()调用之后增加任何初始化

End Sub

'窗体重写处置以整理组件列表。

Protected Overloads Overrides Sub Dispose(ByVal disposing

As Boolean)

If disposing Then

If Not(components Is Nothing)Then

components.Dispose()

End If

End If

MyBase.Dispose(disposing)

End Sub大李开端说明道:“MyBase 关键词的行动相似于引证类的当时实例的基类的目标变量。MyBase 常用于拜访在派生类中被重写或躲藏的基类成员。在这段代码中,MyBase指的当然即是

,没什么好说明的。但析构的

办法值得一说。”

他看了我一想,持续说:“Form.Dispose办法是重写自Control.Dispose办法的,那么Control.Dispose 办法的含义又是怎么的?它的效果即是:开释由Control占用的非保管资源,还能够另外再开释保管资源。当它参数中的disposing 为true 则开释保管资源和非保管资源;为false 则仅开释非保管资源。

Form类的disposing为true。在封闭窗体时主动调用dispose的功用是得益于.net的公共语言运转库,运

行库主动处置目标规划和办理对目标的引证,当不再运用目标时释放它们。其生存期以这种办法来办理的目标称为保管数据。主动内存办理消除了内存走漏以及其他一些常见的编程过错。任何类型的Dispose 办法都大概开释它具有的一切资源。它还大概经过调用其父类型的Dispose 办法开释其基类型具有的一切资源。该父类型的Dispose 办法大概开释它具有的一切资源并一样也调用其父类型的Dispose 办法,从而在整个基类型层次布局中传达该办法。要保证始终正确地整理资源,Dispose 办法大概能够被屡次安全调用而不引发任何反常。”

“可是,若是系统疑问或应用程序调用上出了疑问,不能正常调用Dispose怎样办?”我想起了啥,问道。

“若是经过Dispose还开释不洁净或没有调用Dispose,系统的废物收回器会调用目标的Finalize 办法

进行铲除。由于履行Finalize 办法会大大减损功用,所以咱们不会一开端就用它去进行铲除作业。”

大李略微说明了一下。

我总算想起了一个重要的疑问:“若是总是在模块中界说的大局变量来处置,由于拜访规模太大,会不会有安全性的疑问?”

“当然,咱们能够试试其他的处理方案。”总是有备而言。

https://www.sodocs.net/doc/592847774.html,之旅(三)—同享成员

一起对https://www.sodocs.net/doc/592847774.html,充满了猎奇与晓得的巴望。

“除了大局变量外,还有啥好办法吗?”我迫不急待地想知道成果。

“能够运用同享成员。”大李依然是那么淡漠然,“https://www.sodocs.net/doc/592847774.html,如今是撑持真实的面向目标编程,能够承继

、运用多态、同享成员和静态成员。同享成员即是在一切类和所界说派生类的实例之间同享的办法、属性、字段和工作。一切运用类创立的目标都能够拜访一样的数据、同享完结进程,并且收到一样的激起工作。”

“这么好呀,那怎样完结呢?”我好象又回到了课堂上,满脑都是无知的符号。

“很简略,在Public或Private成员润饰符后加上Shared关键词即可了。Shared关键词指示一个或多个被声明的编程元素将被同享。同享元素不关联于某类或布局的特定实例。能够经过运用类名或布局称号或许类或布局的特定实例的变量称号限制同享元从来拜访它们。你来看这段代码

Public Class ShareClass

Public Shared SharedValue As String

Public Shared Sub ShareMethod()

MsgBox("This is a shared method.")

End Sub

End ClassSub

TestShared()

Dim Shared1As New ShareClass()'创立了类的一个实例.

Dim Shared2As New ShareClass()'创立了类的第二个实例.

Shared1.SharedValue="Share Value1"'给同享字段赋值.

Shared2.SharedValue="Share Value2"'重写同享字段的值

ShareClass.ShareMethod()'不必创立实例就能够调用类的办法.

End Sub“有两个关键,一个是同享成员若是是办法或特点,我们不必创立实例就能够直接用‘类名.共

享成员’的办法进行调用;第二个关键,同享字段是仅有的,你来说说运转后Shared1.SharesValue的值是多少?”

我脑筋里转了两圈,已然说同享字段是仅有的,那么Shared1.SharedValue和Shared2.SharedValue其实对应的是同一个同享字段,所以么,我马上大声说:“是Share Value 2。由于被Shared2.SharedValue 修改了。”

“没错!”大李又一次绽放了会意的浅笑,真是对我的最高奖励。“那你还不着手修正一下你的程序?

好!说做就做。已然同享特点在其调用的时分是不必实例化的,那么我最佳能凭借布局一个同享特点来完结。并且要能够判别其实例是不是存在或是不是被毁掉,若是不存在就布局一个新的窗体实例,若是存在

就对其进行调用:

Private

Shared m_vb6FormDefInstance As Form1

Public Shared Property DefInstance()As Form1

Get

If m_vb6FormDefInstance Is Nothing OrElse_

m_vb6FormDefInstance.IsDisposed Then‘判别窗体

实例是不是存在

m_vb6FormDefInstance=New Form1()

End If

DefInstance=m_vb6FormDefInstance

End Get

Set(ByVal Value As Form1)

m_vb6FormDefInstance=Value

End Set

End Property在Form2中也参加相似的代码,只不过把Form1改为Form2 。调用它是如此的便利://Form1中的代码

Private Sub Button1_Click(ByVal sender As System.Object,ByVal e

As System.EventArgs)

Handles Button1.Click

Me.Hide()

End Sub

//Form2中的代码

Private Sub Button1_Click(ByVal sender As_

System.Object,ByVal e As System.EventArgs)_

Handles Button1.Click

Me.Hide()

End Sub

Private Sub Form2_Closing(ByVal sender As Object,_

ByVal e As_

Handles MyBase.Closing

End Sub赶忙运转一下,不对呀,榜首个窗体里文本框改为“Henry的著作”后,再切换仍是不见了?我疑问地昂首看了看大李。

“你再好好想想,首次发动的是啥?”大李模糊地说了一句。

“首次发动的是作为主线程的Form1窗体呀!”我嘟啷着说。

“那么Form1.DefInstance在哪呢?”大李这句话真是吵醒梦中人呀!

我马上着手:

Module Module1

Sub Main()

Application.Run(Form1.DefInstance)

End Sub

End Module并把Module1设为发动项目。运转运转……哈哈,一切都满意!我开端喜爱上https://www.sodocs.net/doc/592847774.html,了。https://www.sodocs.net/doc/592847774.html,之旅(四)—类和布局

刚上班,就被大李找去了。“Henry,昨日对窗体的操作给你最大的领会是啥?”

“当然有领会,最深的形象即是https://www.sodocs.net/doc/592847774.html,中类是无所不在了,连窗体都变成了一个类。”我深有感受地说。

“没错,类是咱们用来布局https://www.sodocs.net/doc/592847774.html,应用程序时的最基本的编程布局了。你也学习过最基本的面向目标编程了,那么你能告诉我,布局与类有啥相似之处与不一样之处吗?”

“好的。”我口中答复着,心里仍是有点不以为然,“布局和类,都是对成员的封装办法,可是类能够

撑持承继……”

大李一边点着头,一边听我说,听到我结尾支吾着没了声响才抬起头“还有呢?”

“没了。”我这时才开端心中发慌。

“呵呵,一样之处我想你心中仍是知道的,它们都富含成员,包括布局函数、办法、特点、字段、常量

、枚举和工作。都可以完结接口,都有同享的布局函数。”

“对不住,结尾那一句,都有布局函数是啥意思?布局的布局函数我从来没有自个界说过。”

大李马上写下了这一段代码:

Structure SHenry

Public x,y As Integer

Public Sub New(ByVal x As Integer,ByVal y As Integer)

Me.x=x

Me.y=y

End Sub

End Structure

Sub main()

Dim H1As SHenry=New SHenry()

Dim H2As SHenry=New SHenry(2,1)

End Sub“真的呢,能够完结布局的布局函数!”我还一向没有注意过这个疑问。“可是,你只界说过

一个带参数的布局函数,H2的实例化我是知道的,可是H1怎样也能经过编译器检测呢?”其实我心中更想问的是布局怎样也能实例化成目标,那和类真的很象呀。

“缘由么,”大李推了推眼镜,“每个布局都隐式地具有Public 无参数实例构造函数,该布局函数产

生布局的默许值。所以你平常不写布局函数,也一样能够New出一个布局来,对吧?事实上,咱们反而在

布局类型声明中不行能声明无参数实例布局函数。只能声明‘参数化’实例布局函数。”

“都能够用new来实例化,布局和类在内存分配上莫非也是一样的吗?”这个疑问我一向挺不知道,正巧借这个论题问一下。

“在这上面,不一样可就大了。”看到大李满面笑容的姿态,我就晓得问到点子上了,马上摆开姿势,作仔细倾听状。

“简略来说,布局是值类型,而类是引证类型。因而,布局运用仓库分配,类运用堆分配。”

看到我苍茫的双眼,大李笑了笑,在电脑上飞快地写了个示例:

Class CHenry

Public z As Integer=0

‘能对非静态成员初始化也是一个差异

End Class

Sub main()

Dim H1As SHenry=New SHenry(0,2)'赋给H1.x=0

Dim H2As SHenry=H1

H2.x=26

Dim R1As New CHenry()'R1.z也是等于0

Dim R2As CHenry=R1

R2.z=26

Console.WriteLine("H1.x="&H1.x&",H2.x="&H2.x)

Console.WriteLine("R1.z="&R1.z&",R2.value="&R2.z)

End Sub“你看一下成果大概是啥?”大李一边说,一边运转了程序:

H1.x=0,H2.x=26

R1.z=26,R2.value=26 “这即是值类型和引证类型的不一样。布局的实例H2.x 赋值并不影响H1.x ,这是由于尽管它们同归于一种SHenry布局,但它们都有各自的存储空间。相反,给R2.z赋值26后; 则会影响R1 和R2 都引证的目标”

“说得更知道一点,类作为引证类型,是存储在运转时的堆上,只能经过引证该存储来拜访它们,不能直接拜访。引证类型的变量总是包括该类型的值引证,或包括空引证。空引证不引证任何内容;除分配空引证外,对空引证进行的任何操作都是无效的。引证类型的变量赋值只会创立引证的一个副本,而不是所引证的值的副本。它们实践上都是会指向同一块存储区的。”大李手指了指运转的成果。

“布局是直接存储在仓库上,要么在数组中,要么在另一个类型中。当包括布局实例的方位被毁掉时,

布局实例也会被毁掉。值类型总是能够直接拜访。咱们不能创立对值类型的引证,也不能引证已毁掉的值类型实例。值类型的变量总是包括此类型的值。与引证类型不一样,值类型的值不能为空引证,也不能引证派生附近程度较大的类型的目标。值类型的变量赋值会创立所赋的值的副本,当然会新拓荒一块内存区来保管值。”

“哦,我知道了。它们还有啥差异没有?”我对布局和类的差异首次有了深入的感受。

“当然有许多,比方一切的布局成员都默以为Public,类变量和常量默以为Private,其他的类成员默

以为Public;布局成员不能声明为Protected,类成员能够;布局进程不能处置工作,类进程能够;结

构变量声明不能指定初始值、New 关键词或数组初始巨细,类变量声明能够。”大李喝了口水,停了一下,然后持续说。

“布局从不停止,所以公共言语运行库从不在任何布局上调用Finalize 办法;类可由废物收回器停止

,废物收回器会盯梢未完结的引证直到某个特定的实例,当检测到没有剩余的活动引证时,废物收回器将在类上调用Finalize。”

“这个我能够知道,由于布局是值类型,是由系统统一办理内存,不是引证,所以不会对内存形成危害。”我接着说了两句。

“还有,你方才也说到了它们之间一个很重要的差异:布局是不行承继的,而类能够承继。其实布局自

身是从ValueType 类隐式承继下来的。数据类型可分为值类型和引证类型。值类型要么是仓库分配的,要么是在布局中以内联办法分配的。引证类型是堆分配的。引证类型和值类型都是从结尾的基类Object 派生出来的。当值类型需求充任目标时,就在堆上分配一个包装,该包装能使值类型看上去像引证目标一样,并且将该值类型的值仿制给它。该包装被加上符号,以便系统晓得它包括一个值类型。这个进程

称为装箱,其反向进程称为撤销装箱。装箱和撤销装箱能够使任何类型像目标一样进行处置。”

“哦,我知道为啥布局也能被实例化成目标了!”我心中喜不自禁。“类的承继我在用C++和Java时也

常常运用,可是VB6是不撑持承继的,前次您说到https://www.sodocs.net/doc/592847774.html,能够撑持承继了,它是怎样做到的呀!”https://www.sodocs.net/doc/592847774.html,之旅(五)—类的承继

“类的承继?”大李点答应,“从前许多人批判VB的功用不完善时,都会加上这个条件。可是如今https://www.sodocs.net/doc/592847774.html, 中现已是比较完善的面向目标编程办法了。方才咱们也说到过,类与https://www.sodocs.net/doc/592847774.html, 的布局相似,让咱们能够界说封装一组有关项的数据类型。然而与布局的一个重要差异即是,https://www.sodocs.net/doc/592847774.html, 类能够承继和扩展其他类的特性。作为新类的根底的类称为‘基类’。从基类派生的类称为‘派生类’。派生类承继基类中界说的一切字段、特点、办法和工作。你看,咱们只需对类进行一次开发和调试,就能够将其作为其他类的基础重复运用。”

“类的承继的概念我学过的,在https://www.sodocs.net/doc/592847774.html,中是如何来实践演练呢?”我仍是坚持着问。

“我写段代码给你看一下吧,俗语不是说,百闻不如一见吗?”大李戏弄了两句,噼噼啪啪地开端敲打起键盘来:

Public Class CBaseHenry

Private x As Integer=10

Protected y As Integer

Sub New()

Console.WriteLine("基类的布局")

End Sub

Protected Overrides Sub Finalize()

Console.WriteLine("基类的析构")

MyBase.Finalize()

End Sub

Public Overridable Function GetY(ByVal x As Integer)As Integer

Me.y=me.x+x‘private类型的me.x只能在基类中运用

Console.WriteLine("基类的GetY办法,成果为:"&Me.y)

Return Me.y

End Function

Public Sub OldY()

Console.WriteLine("基类的OldY办法")

End Sub

End Class“你看,这是一个基类,我再给你写个承继自该类的派生类。”他一边说一边持续写:Public Class

CDerivedHenry

Inherits CBaseHenry

Sub New()

MyBase.New()‘注意:这句话要放在sub内的榜首句

Console.WriteLine("派生类的布局")

End Sub

Protected Overrides Sub Finalize()

Console.WriteLine("派生类的析构")

MyBase.Finalize()

End Sub

Public Overrides Function GetY(ByVal x As Integer)As

Integer

Me.y=x*10‘protected类型的me.y却能够在派生类中使

Console.WriteLine("派生类的GetY办法,成果为:"&Me.y)

Return Me.y

End Function

Public Sub NewY()

Console.WriteLine("派生类的新办法")

End Sub

End Class“你看清了吧,咱们经过声明Inherits关键词,来标识该类的基类是谁,若是没有标识的类,比方CBaseHenry,https://www.sodocs.net/doc/592847774.html,就会视其为派生自Object基类,这是种隐式承继,和布局是隐式承继自ValueType 类的办法是一样的。”大李指着屏幕跟我说明着。

“那这些overrides与overload标识是啥意思呀?”我隐约感受到它们的含义,却无法知道地说出来。“咱们运用承继,也要答应派生类中的某些特点或办法有与基类不一样的行动,咱们想‘重写’它,但基类也得要附和你才干重写呀,不然不就会发作命名抵触了吗?由于派生类的目标是能够运用基类的共用成员的,那怎样晓得哪些基类的办法与特点是被派生类重写了呢?所以就有必要有一个约好:咱们在基类顶用Overridable润饰符来标识答应基类中的特点或办法在其派生类中被重写,没有标识的其实即是默以为有NotOverridable润饰符来隐式地标识,用于提示编译器该特点或办法不行被重写;然后在派生类重写时,咱们就用Overrides润饰符来标识哪些是重写自基类中界说的Overridable 特点或办法。”大李娓娓道来,“咱们来演练一下对派生类的操作吧。”

Public Sub Main()

Dim obj As CDerivedHenry=New CDerivedHenry()

obj.GetY(4)'调用派生类的GetY办法

obj.OldY()'调用基类的oldY办法

obj.NewY()'调用派生类的NewY办法

End Sub然后大李按了一下F5键,在“输出”窗口中就呈现了如下的运转成果:

基类的布局‘开

始运转New CDerivedHenry()

派生类的布局

派生类的GetY办法,成果为:40‘obj.GetY(4)的运转成果=4*10,

不等于基类的(4+10)

基类的OldY办法‘obj.OldY()的运转成果

派生类的新办法‘obj.NewY()的运转成果

派生类的析构

基类的析构“你看,你只在Main中实例化了一个派生类的对象,为啥先会呈现‘基类的布局’?”大李问。“这个么,”我开端在代码中检查起来,“是这句了。”我手指着派生类里的这段代码:

Sub New()

MyBase.New()‘注意:这句话要放在sub内的榜首句

Console.WriteLine("派生类的布局")

End Sub“没错,咱们有必要要注意这一点,即是派生类的布局函数与析构函数都有必要从头编写。New 布局函数通常用于翻开文件、衔接到数据库、初始化变量以及处置任何需求在可运用目标前完结的其他使命。咱们有必要在Sub New 布局函数中的榜首行代码运用句子MyBase.New(),来调用类层次布局中该类的基类的布局函数,以取得基类的性质。析构则是在Sub Finalize中履行完对派生类的的整理使命,如保管状况信息、封闭文件和与数据库的衔接,以及履行在开释目标前有必要完结的其他使命之后,在析构函数的结尾一句运用句子MyBase.Finalize() 显式调用其基类的Sub Finalize 办法,以析构MyBase.New ()布局的内容。所以你从程序运转成果中也能够很知道的看出这一次序。” 大李敲击着屏幕,象是给我提个醒。

“知道了,我会记取的。”我诚实地址头回应着。

大李俄然想起了啥,昂首对我说:“讲到类的承继,咱们还得看一下重载与躲藏的疑问。”

https://www.sodocs.net/doc/592847774.html,之旅(六)—重载与躲藏

大李坐在转椅上左右晃动着,手中的一个硬币在他指间灵活地翻滚着。“hi, Henry。你对重写与重载的意思知道是怎么的呢?”

我正盯着他手中的硬币发呆呢,“哦,重写,即是您方才举的示例中,在派生类顶用Overrides从头编写有Overridable标识的基类的办法或特点;重载么,即是咱们能够用一样的称号,在一个类顶用不一样的参数列表来创立多个办法和属性,在调用时就能够习惯不一样参数类型的需求。”

“在一个类中创立?”大李的左眉向上一挑,我就晓得我能够说错了。可是,好象没有说错啥呀。

“那好,你看一下,这样的写法会有啥成果?”

Public Class CBaseHenry

Public Sub oldY()

Console.WriteLine("基类的oldY办法")

End Sub

End Class

Public Class CDerivedHenry

Inherits CBaseHenry

Public Overloads Sub oldY(ByVal j As Integer)

Console.WriteLine("派生类的oldY办法")

End Sub

End Class

Public Sub Main()

Dim obj As CDerivedHenry=New CDerivedHenry()然后大李写了obj.oldY这时呈现的智能感知菜单上呈现了参数列表:

“咦,第二个oldY()无参数进程签名大概是基类界说的呀。为什么在派生类的实例中也会呈现呀!”我不由猎奇起来。

“没错。还记得我前次曾显式编写布局函数的事吗?我用了一个mybase.new()用于承继下基类的性质。如今没有写,其实是一种默许的隐式调用。”大李一说我就知道了,其实如今的派生类大概是内含两个oldY 办法了,无参数的oldY()其实就是基类的办法,是由于Overloads和Mybase.new()两层效果的影响。“那你再看这样的改变状况。”大李象是看出我现已知道了这个疑问。他在派生类中增加了一个用Overloads 标识的与基类办法共同的无参数oldY()。

Public Class CBaseHenry

Public Sub oldY()

Console.WriteLine("基类的oldY办法")

End Sub

End Class

Public Class CDerivedHenry

Inherits CBaseHenry

Public Overloads Sub oldY(ByVal j As Integer)

Console.WriteLine("派生类的oldY办法1")

End Sub

Public Sub oldY()

Console.WriteLine("派生类的oldY办法2")

End Sub

End Class

Public Sub Main()

Dim obj As CDerivedHenry=New CDerivedHenry()

obj.oldY()

End Sub“好的,如今你再说说看,如今的obj.oldY()的运转成果会打印出啥?”大李手按在F5运转键上问我。

“大概是派生类的,不对,基类,呃,派生类……”我一会儿就晕了。

“呵呵。”大李也不由发笑起来。然后点击了一下F5键。成果是:

派生类的oldY办法2“这就叫躲藏,咱们用overloads办法,躲藏了基类的同名办法。以防用户发作混杂。通常来说,躲藏有两种状况,一种是经过规模来完结。比方你界说一个大局变量x,但在一个办法中,你又界说了一个局部变量x,在办法中运用x时,运用的是局部变量的那一个,也即是用局部变量x在办法中躲藏了大局变量x。另一种状况,即是经过承继来隐藏,办法么,除了方才用的overloads,还能够用Shadows关键词来完结。”

“Shadows?暗影?却是很恰当的姓名。这个是怎样用的呢?”我爱好由然而生。

“Shadows功用很强的。”说着,大李又开端修正方才的代码了。

Public Class CBaseHenry

Public Sub oldY()

Console.WriteLine("基类的oldY办法")

End Sub

End Class

Public Class CDerivedHenry

Inherits CBaseHenry

Public Shadows Sub oldY(ByVal j As Integer)

Console.WriteLine("派生类的oldY办法")

End Sub

End Class

Public Sub Main()

Dim obj As CDerivedHenry=New CDerivedHenry()再写到obj.oldY时,呈现的智能感知菜单上就剩余一个办法了。

“哈,真的呢,基类的办法看不到了。可是,这和重写的效果岂不是一样了吗?”我不由地又想起一个疑问,若是是一样的效果,要Shadows干啥呀。

“仍是有差异的。”大李开端扳着指头数起来,“最显着的差异在于,躲藏适用于任何元素类型,你能够在派生类中用Public Shadows oldY as Integer来躲藏基类的oldY()办法;而重写只能适用于办法与特点,并且声明参数与润饰符都需求完全共同。”

“ 还要注意一点,当派生类又派生出一个子类时,重写与躲藏都会被承继下去。当然,若是在派生类中是用private来润饰Shadows成员的话,它的子类就会承继它基类的成员了。”

“我却是感受,如今https://www.sodocs.net/doc/592847774.html,对承继的处置功能真的很健壮,有承继,关于编程者来说,真的是件功德。”我不由感叹道。

“不错,承继层次系统是有许多优点,可是事物总是有其双面性,承继也有不少疑问,其间最费事的,即是‘软弱的基类’。” 大李紧闭眉头仔细地说。

“哦,啥叫‘软弱的基类’ ?”我不解地问。

https://www.sodocs.net/doc/592847774.html,之旅(七)—软弱的基类

“已然说是软弱,当然是指它象蛋壳一样一触即溃喽。这个疑问其实很好知道。程序总是由人来描绘与编写的,所以作业开端时思考不到某些疑问当然也是很正常的事。所以能够在作业进行了一段时间后发现基类需求改变。你想,若是我在基类中更改了成员的数据类型,以及那些答应重写的那些办法和特点,那派生类及其子类还能正常作业吗?尤其是当一个团队中的多个开发人员一起来创造基类和派生类时,就更是要命了。许多状况下,咱们能够现已把基类和一些派生类编译成二进制办法进行提交了。更改基类,从头编译再散布,会牵一发而动全身,致使项目的溃散。所以咱们把这叫做‘软弱的基类’。也就是说,它是整个工程中最单薄最丧命的环节。”大李眉头一向紧闭着,想必是回想起了自个受冲击的阅历。

“这么严峻呀,如今的软件工程设计办法会不会对这个有极好的处理方案?”我尽力想减轻一下大李的严厉神情。

“若是对项目的前期描绘思考尽能够周详,在工程施行中对项目的代码操控与有关性剖析做得结壮,会起到极好的效果。可是不论一个人怎么尽力,有时仍是无法防止对基类进行不行预见的更改。咱们探索过好久,有了一些处置的手法。”

“真是成事在人呀,咱们如今有啥处理之道?”我也一会儿振奋起来了。

“呵呵,并不是啥完满处理方案。只能在某种程度上减轻损害。咱们常用的一个办法,最直接的思维即是,把有能够发作的更改全都放在派生类中进行,不在基类中做。”

“ 这详细是啥意思呀,我仍是不太知道。”我不好意思地挠犯难。

大李浅笑着点答应,看来是晓得我不会知道的了。“咱们在基类中运用的是抽象类,它内含的办法与特点只要界说,没有进行完结,而把完结有些都放在派生类中做。这样一来,抽象类本身是无法被实例化的。可是它的优点显而易见,即是有能够发作的完结上的更改都会只涉及到它的派生类了。https://www.sodocs.net/doc/592847774.html,中就供给了这样的手法。”

说着,大李就翻开https://www.sodocs.net/doc/592847774.html,集成编译环境,随手写了一小段代码:

Public MustInherit Class CBaseHenry

Public MustOverride Sub subX(ByVal x As Integer)

Public MustOverride Function fcnY(ByVal y As Integer)As

Long

End Class

Public Class CDerivedHenry

Inherits CBaseHenry

Public Overrides Sub subX(ByVal x As Integer)

'写入完结的代码

End Sub

Public Overrides Function fcnY(ByVal y As Integer)As Long

'写入完结的代码

End Function

End Class“这里要注意两个疑问,一个是关键词,咱们用MustInherit来润饰类名,使类变成抽象类,在它的成员中,把办法和特点前参加MustOverride润饰符表明它们有必要在派生类中加以完结。第二个要注意的是,派生类有必要对一切用MustOverride标识的基类办法和特点都进行完结,只重写了subX,不写fcnY编译器会报错的。”

“这确实能够处理一有些疑问,但好象只能处理在基类中进行完结的代码有更改的疑问,关于数据类型的更改好象没有啥效果。”我看了好一会,宣布了这样的疑问。

“所以我方才说,是在某种程度上进行处理嘛。”大李也不由笑了起来,“不过你提的这个疑问,倒不是太费事,咱们能够在派生类顶用Shadows来处理呀!(

这却是个不错的主见,我心中暗暗评估了一番。俄然我又想到一个疑问:“若是基类要做功用拓展,怎样办呀?”

“若是是要做拓展,最安全的方法是增加新成员,而不是对基类的大举修正。通常是往派生类增加描绘时缺失的新成员。最佳不要运用Overloads关键词来命名与基类一样的成员,那样往往会带给你意想不到的疑问。最佳从头界说新成员,命名上也要尽量与基类已有的成员名区别开来。其实,也能够往抽象类基类中增加新成员的界说,但这样一来,需要为基类拟定版别,尽管不会对应用程序形成毁灭性的损害,可是大概要能够完全地操控与办理自个的代码。咱们通常是不期望拓展基类的。”

我现已粗心上领会了大李的一片苦心:“您的意思,是不是指基类的软弱疑问实践上是客观存在的,咱们所做的即是要最大程度的减小这个疑问带来的损害?”

大李眼里闪过一丝赞赏的笑意,颌首道:“没错,关于一个应用程序的描绘者来讲,想运用面向目标办法来开发,有必要要在描绘的时分精心策划类的层次布局。通常来说,是有这样几个准则需求掌握的:

榜首,遵循先通用,再专用的准则。先描绘好层次布局中每一级另外类中的通用有些,也即是供给给派生类承继的成员和标识为Public的成员;

第二,在界说数据类型和存储区时要有预留量,以防止今后更改艰难。例如,即便当时数据能够仅需求Integer类型就够了,在描绘时咱们运用Long 类型的变量。当然,最佳能物尽其用,也不要盲目扩大;

第三,在一个项目中,有必要统一办理与分配团队中运用的一切的命名,以削减命名抵触,这一点其实事关重大;

第四,要运用供给可行的最低拜访权限的拜访润饰符声明类成员。内部类成员应声明为Private;仅在类内与派生类才需求的成员应符号为Protected;Friend 数据成员能够从类的外部拜访,但仅限于该模块是界说该类的项目的一个组成有些;运用Public标识的成员,只能是实例化时真实需求的内容,并且常常用在类层次布局的底部。”

“也即是说,一个标准的操作,标准的命名系统可以决议基类的健壮与否?”我不由感受了一声。

“不对,大概这样说,能够决议的是给软弱的基类穿上多厚的防护衣。由于基类一向都是软弱的。”大李更正路。

我连声附和:“对,对。我如今是真实明白为啥总有人提编程标准的工作,我一向以为是增强代码的可读性,没想到,对程序本身还有这么大的协助。”

“当然,其实你仔细想一下,Overrides关键词的效果,不论要不要注明,编译器都能够很便利地判别办法或特点是不是在基类中,签名是不是匹配,可是https://www.sodocs.net/doc/592847774.html,需求咱们有必要标示,即是强迫开发人员注明重载基类办法或特点的目的,使开发进程更合理与有用。此外,还有更重要的即是,咱们要在工程实践中不断地学习与锻炼,晓得更多的常识,取得更多的经历,这样才会生长为一名合格的程序描绘师。就拿承继来说吧,在.NET 中其实撑持三种承继办法:完结承继、接口承继、可视承继。咱们其实只用了第一种承继办法,你看,要学的东西是不是许多?”

相关主题