VB 文本框怎么这样显示真是奇怪le吧地图下载

henry vb 学习
阅读:106次&&&时间: 00:00:00&&
HenryVB.NET— 很久没有新作,架不住朋友们的询问与追问,只得拿些旧作来补上近日没空写作的不足。惭愧ing!这一系列与Henry手记系列不同,是一个角色形式的情景故事,最初发表于《计算机世界》,适用于VB.net初学者,老鸟们就不用在我这一系列的文章上浪费时间了。我希望自己有时间以后,能不断继续这一系列的文章,加入大量的工程解决方案。同时,我的Henry手记也会继续写下去,以回报长期以来支持我的所有朋友。&背景:我叫Henry,刚从大学计算机专业毕业,新加入索易公司的.NET开发小组;负责带我的头目是大李,.NET高手,是个幽默又有耐心的家伙;老白是我们部门的头头,经常带给我们古怪又难缠的需求。Me.HideForm2.ShowMe.HideForm1.Show在代码段中故计重施,当我想写Form2.Show时,在Form2之后打个点,出现的智能感知菜单中,却没有Show这一方法。真奇怪,这怎么可能?难道VB.NET改动有这么大。我抬头看了看站在我身旁的大李。大李立刻面露微笑,看来是正中他的圈套。他手指向屏幕,点了点Form1中的代码的开头处: Form1Inherits System.Windows.Forms.Form不笨的我立刻就明白了,现在的Form1是一个从System.Windows.Forms.Form继承下来的类了,调用的时候当然需要先实例化了,哈,也不难嘛。马上动手: Button1_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles Button1.ClickDim frm2 As New Form2() ‘在Form2的Button2的单击事件中也相似地写了一段代码,运行一下,和VB6的效果差不多了。正在得意中,大李走过来,在我的Form1中添加了一个文本框textbox1,清空其中的文字。然后运行了一下程序。在运行中,他在文本框里写了几个字:“Henry的作品”,然后点击Button1切换到Form2,接着点击Button2,重新出现的Form1的文本框中却变成了空白。“这……”我一时语塞,慢慢地想起点什么,“应该是程序通过New构造方法,每次点击按钮都会重新实例化一次Form类吧?所以每次重新出现的窗体都不是以前曾经出现过的那个。那以前的窗体也就失踪了,调不出来了。”“失踪?”大李不禁笑了起来,“对呀,失踪在内存中。你每实例化一次,系统就会开辟一块内存给一个对象,并会有相应的线程对其进行控制与管理。如果作为进程中主线程的第一个Form1被Hide起来,没有关闭它,那么即使你关闭后来新创建的所有窗体,进程也不会被中止。那么,用什么方法去找回你失踪的窗体,或者说怎么样就只使用一个对象,不再每次实例化呢?”“全局对象!”我脱口而出,感觉到大李不置可否,只好硬着头皮继续说:“利用全局变量,使Form1与Form2只实例化一次,这样就可以了。”然后,在大李的注视下,我开始实施我的计划:再创建一个模块文件:module1.vb,并且同时在:解决方案管理器-〉项目名.sln -〉右点鼠标 -〉属性 -〉通用属性-&常规-&启动对象 -〉改为Module1。让程序启动后先运行Module1中的代码指令,然后编写代码如下:'Module1.vbModule Module1&&& Public frm1 As New Form1()& ‘定义Form1的公用变量实例&&& Public frm2 As New Form2() ‘定义Form1的公用变量实例&&& Sub Main()&&&&&&& Application.Run(frm1) ‘这句话表明程序启动了modele后,会接下来运行frm1,即form1的一个实例。主线程就成为运行期的frm1了。&&& End SubEnd Module这样一来,frm1、frm2就成为全局变量了,在Button1与Button2的单击响应代码中只要写:frm1.show就可以了。再运行程序,可以了,“Henry的作品”一直都会保持在Form1中的文本框里。大李老哥没等我欢笑起来就再次出手,在frm1隐藏起来的时候,关闭了Form2窗口(frm2实例),桌面上什么窗体都没有了,进程又一次被陷入在无法中止的情况下。My God,面向对象的VB.NET,爱你难道真的这么难……HenryVB.NET()— &大李在Form1窗体的实例被隐藏的时候,关闭了Form2窗体的实例,使我失去了对主线程的人工控制,进程无法正常关闭了。只好使用Ctrl+Alt+Del调出系统进程管理器,强行中止了该进程。为了避免抬头看见大李的笑脸,我只好低头想办法。有了,我只要能截获Form2实例关闭的消息,不就可以再调出隐藏的主线程窗体了吗?在Form2的基类事件(Base Class Event)中重载Closing方法进行处理: Form2_Closing(ByVal sender As Object, ByVal e As _ MyBase.Closingfrm1.Show()End Sub哈,很方便,我关闭了Form2窗体的实例后,被隐藏的那个frm1又出现了。“嗯,”大李终于发话了,“你再点击一下Form1窗体上的Button1试试。”我背心一凉,隐隐感觉大李等待着的就是这个时候。无奈的我还只能照他说的去做。果然,弹出一个出错窗口:“你关闭了frm2这个Form2的实例,也就结束了这个对象的生存期,”大李看来是蓄势已久了,“这就是出错提示中所说的‘无法访问名为Form2的已处置对象’。frm2Form2Dispose”“所以我只要判断一下frm2是否被销毁就行了,如果销毁了,我就再构造一个实例不就行了?”我恍然大悟道。大李第一次微笑地点了点头说:“用frm2.IsDisposed就可以来判断了。”我心领神会地写道: Button1_Click(ByVal sender As System.Object, _ System.EventArgs) Handles Button1.Click&&&&&& If frm2 Is Nothing Or frm2.IsDisposed Then& ‘frm2 = New Form2()End IfMe.Hide()frm2.Show()End Sub这下完善多了,两个窗体之间的切换也不会有这么多别扭的问题了。我转过身,看到大李已经找了把椅子坐在我的身边。“你来说说,对VB.NET的窗体实例的创建与销毁的过程吧。”我整理了一下凌乱的思路,长吁了一口气,开始说:“一个窗体类,比如Form1类是通过调用其基类,就是Form类的New方法来创建实例、Dispose方法来销毁实例。 New()MyBase.New()'该调用是 Windows 窗体设计器所必需的。InitializeComponent()'在 InitializeComponent() 调用之后添加任何初始化End Sub'窗体重写处置以清理组件列表。Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)If disposing ThenIf Not (components Is Nothing) Thencomponents.Dispose()End IfEnd IfMyBase.Dispose(disposing)大李开始解说道:“MyBase 关键字的行为类似于引用类的当前实例的基类的对象变量。MyBase 常用于访问在派生类中被重写或隐藏的基类成员。MyBaseSystem.Windows.Forms.FormNewForm.DisposeControl.DisposeControl.DisposeControldisposing
Formdisposingtruedispose.net Dispose
Dispose Dispose DisposeDisposeDispose Finalize
Finalize &我开始佩服起大李来了,同时对VB.NET充满了好奇与了解的渴望。“除了全局变量外,还有什么好办法吗?”我迫不急待地想知道结果。“可以使用共享成员。”大李依然是那么淡淡然,“VB.NET现在是支持真正的面向对象编程,可以继承、使用多态、共享成员和静态成员。共享成员就是在所有类和所定义派生类的实例之间共享的方法、属性、字段和事件。所有使用类创建的对象都可以访问相同的数据、共享实现过程,并且收到相同的激发事件。”“这么好呀,那怎么实现呢?”我好象又回到了课堂上,满脑都是无知的符号。“很简单,在Public或Private成员修饰符后加上Shared关键字即可了。Shared关键字指示一个或多个被声明的编程元素将被共享。共享元素不关联于某类或结构的特定实例。可以通过使用类名或结构名称或者类或结构的特定实例的变量名称限定共享元素来访问它们。你来看这段代码&Public Class ShareClass&& && Public Shared SharedValue As String&& && Public Shared Sub ShareMethod()&&&&& &&&&& MsgBox("This is a shared method.")&&& && End SubEnd ClassSub TestShared()&& && Dim Shared1 As New ShareClass()&& ' 创建了类的一个实例.&& && Dim Shared2 As New ShareClass()&& '创建了类的第二个实例.&& && Shared1.SharedValue = "Share Value 1"&&& '给共享字段赋值.& && Shared2.SharedValue = "Share Value 2"&& ' 重写共享字段的值&& && ShareClass.ShareMethod()  && '不用创建实例就可以调用类的方法.End Sub&“有两个要点,一个是共享成员如果是方法或属性,我们不用创建实例就可以直接用‘类名.共享成员’的方法进行调用;第二个要点,共享字段是唯一的,你来说说运行后Shared1.SharesValue的值是多少?”我头脑里转了两圈,既然说共享字段是唯一的,那么Shared1.SharedValue和Shared2.SharedValue其实对应的是同一个共享字段,所以么,我立刻大声说:“是Share Value 2。因为被Shared2.SharedValue修改了。”“没错!”大李又一次绽放了会心的微笑,真是对我的最高奖赏。“那你还不动手修改一下你的程序?”好!说做就做。既然共享属性在其调用的时候是不用实例化的,那么我最好能借助构造一个共享属性来实现。并且要能够判断其实例是否存在或是否被销毁,如果不存在就构造一个新的窗体实例,如果存在就对其进行调用: m_vb6FormDefInstance As Form1 Property DefInstance() As Form1Get Nothing OrElse _m_vb6FormDefInstance.IsDisposed Then   m_vb6FormDefInstance = New Form1()  End IfDefInstance = m_vb6FormDefInstanceEnd GetSet(ByVal Value As Form1)m_vb6FormDefInstance = ValueEnd Set&在Form2中也加入相似的代码,只不过把Form1改为Form2。调用它是如此的方便: Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.ClickMe.Hide()Form2.DefInstance.Show() Button1_Click(ByVal sender As _ e As System.EventArgs) _Me.Hide()Form1.DefInstance.Show()End Sub Form2_Closing(ByVal sender As Object,_ ponentModel.CancelEventArgs) _Handles MyBase.ClosingForm1.DefInstance.Show()End Sub&赶紧运行一下,不对呀,第一个窗体里文本框改为“Henry的作品”后,再切换仍是不见了?我疑惑地抬头看了看大李。“你再好好想想,第一次启动的是什么?”大李模糊地说了一句。“第一次启动的是作为主线程的Form1窗体呀!”我嘟啷着说。“那么Form1.DefInstance在哪呢?”大李这句话真是惊醒梦中人呀!我立刻动手:Sub Main()Application.Run(Form1.DefInstance)& End Sub&并把Module1设为启动项目。运行运行……哈哈,一切都如意!我开始喜欢上VB.NET了。Henry的VB.NET之旅(四)—类和结构&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 韩睿&刚上班,就被大李找去了。“Henry,昨天对窗体的操作给你最大的体会是什么?”“当然有体会,最深的印象就是VB.NET中类是无所不在了,连窗体都成为了一个类。”我深有感触地说。“没错,类是我们用来构造VB.NET应用程序时的最基本的编程结构了。你也学习过最基本的面向对象编程了,那么你能告诉我,结构与类有什么相似之处与不同之处吗?”“好的。”我口中回答着,心里还是有点不以为然,“结构和类,都是对成员的封装方式,但是类可以支持继承……”大李一边点着头,一边听我说,听到我最后支吾着没了声音才抬起头“还有呢?”“没了。”我这时才开始心中发慌。“呵呵,相同之处我想你心中还是明白的,它们都含有成员,包括构造函数、方法、属性、字段、常量、枚举和事件。都可以实现接口,都有共享的构造函数。”“对不起,最后那一句,都有构造函数是什么意思?结构的构造函数我从来没有自己定义过。”大李立刻写下了这一段代码:&Public x, y As IntegerPublic Sub New(ByVal x As Integer, ByVal y As Integer)Me.x = xMe.y = yEnd SubDim H1 As SHenry = New SHenry()Dim H2 As SHenry = New SHenry(2, 1)&“真的呢,可以实现结构的构造函数!”我还一直没有注意过这个问题。“可是,你只定义过一个带参数的构造函数,H2的实例化我是明白的,可是H1怎么也能通过编译器检测呢?”其实我心中更想问的是结构怎么也能实例化成对象,那和类真的很象呀。“原因么,”大李推了推眼镜,“每个结构都隐式地具有 Public 无参数实例构造函数,该构造函数产生结构的默认值。所以你平时不写构造函数,也一样可以New出一个结构来,对吧?事实上,我们反而在结构类型声明中不可能声明无参数实例构造函数。只能声明‘参数化’实例构造函数。”“都可以用new来实例化,结构和类在内存分配上难道也是一样的吗?”这个问题我一直挺不明白,正好借这个话题问一下。“在这上面,差别可就大了。”看到大李喜笑颜开的样子,我就知道问到点子上了,立刻摆开架势,作认真倾听状。“简单来说,结构是值类型,而类是引用类型。因此,结构使用堆栈分配,类使用堆分配。”看到我迷茫的双眼,大李笑了笑,在电脑上飞快地写了个示例:&Public z As Integer = 0‘Dim H1 As SHenry = New SHenry(0, 2) '赋给H1.x=0Dim H2 As SHenry = H1H2.x = 26Dim R1 As New CHenry() 'R1.z也是等于0Dim R2 As CHenry = R1R2.z = 26Console.WriteLine("H1.x= " & H1.x & ",H2.x= " & H2.x)Console.WriteLine("R1.z= " & R1.z & ",R2.value= " & R2.z)&“你看一下结果应该是什么?”大李一边说,一边运行了程序:&大李看着我瞪圆的双眼,慢慢地说:“这就是值类型和引用类型的差别。结构的实例 H2.x 赋值并不影响H1.x,这是因为虽然它们同属于一种SHenry结构,但它们都有各自的存储空间。相反,给 R2.z26; 则会影响R1 和 R2 都引用的对象”“说得更清楚一点,类作为引用类型,是存储在运行时的堆上,只能通过引用该存储来访问它们,不能直接访问。引用类型的变量总是包含该类型的值引用,或包含空引用。空引用不引用任何内容;除分配空引用外,对空引用进行的任何操作都是无效的。引用类型的变量赋值只会创建引用的一个副本,而不是所引用的值的副本。它们实际上都是会指向同一块存储区的。”大李手指了指运行的结果。“结构是直接存储在堆栈上,要么在数组中,要么在另一个类型中。当包含结构实例的位置被销毁时,结构实例也会被销毁。值类型总是可以直接访问。我们不能创建对值类型的引用,也不能引用已销毁的值类型实例。值类型的变量总是包含此类型的值。与引用类型不同,值类型的值不能为空引用,也不能引用派生相近程度较大的类型的对象。值类型的变量赋值会创建所赋的值的副本,当然会新开辟一块内存区来保存值。”“哦,我明白了。它们还有什么区别没有?”我对结构和类的区别第一次有了深刻的感觉。“当然有很多,比如所有的结构成员都默认为 Public,类变量和常量默认为 Private,其他的类成员默认为 Public;结构成员不能声明为 Protected,类成员可以;结构过程不能处理事件,类过程可以;结构变量声明不能指定初始值、New 关键字或数组初始大小,类变量声明可以。”大李喝了口水,停了一下,然后继续说。结构从不终止,所以公共语言运行库从不在任何结构上调用 Finalize 方法;类可由垃圾回收器终止,垃圾回收器会跟踪未完成的引用直到某个特定的实例,当检测到没有剩下的活动引用时,垃圾回收器将在类上调用 Finalize。”“这个我可以理解,因为结构是值类型,是由系统统一管理内存,不是引用,所以不会对内存造成危害。”我接着说了两句。“还有,你刚才也提到了它们之间一个很重要的区别:结构是不可继承的,而类可以继承。其实结构自身是从 ValueType 类隐式继承下来的。 Object C++JavaVB6VB.NETHenry的VB.NET之旅(五)—类的继承&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 韩睿&“类的继承?”大李点点头,“以前很多人批评VB的功能不完善时,都会加上这个条件。但是现在VB.NET中已经是比较完善的面向对象编程方法了。刚才我们也提到过,类与 VB.NET 的结构类似,让我们可以定义封装一组相关项的数据类型。然而与结构的一个重要区别就是,VB.NET 类可以继承和扩展其他类的特性。作为新类的基础的类称为‘基类’。从基类派生的类称为‘派生类’。派生类继承基类中定义的所有字段、属性、方法和事件。你看,我们只要对类进行一次开发和调试,就可以将其作为其他类的基础重复使用。”“类的继承的概念我学过的,在VB.NET中是如何来实际演练呢?”我还是坚持着问。“我写段代码给你看一下吧,俗话不是说,百闻不如一见吗?”大李调侃了两句,噼噼啪啪地开始敲打起键盘来: CBaseHenryPrivate x As Integer = 10Protected y As IntegerSub New()Console.WriteLine("基类的构造")End SubProtected Overrides Sub Finalize()Console.WriteLine("基类的析构")MyBase.Finalize()End Sub Function GetY(ByVal x As Integer) As Integer‘Console.WriteLine("基类的GetY方法,结果为:" & Me.y)Return Me.yEnd FunctionPublic Sub OldY()Console.WriteLine("基类的OldY方法")&“你看,这是一个基类,我再给你写个继承自该类的派生类。”他一边说一边继续写: CDerivedHenryInherits CBaseHenrySub New()MyBase.New() ‘Console.WriteLine("派生类的构造")End SubProtected Overrides Sub Finalize()Console.WriteLine("派生类的析构")MyBase.Finalize()End SubPublic Overrides Function GetY(ByVal x As Integer) As IntegerMe.y = x * 10 ‘Console.WriteLine("派生类的GetY方法,结果为:" & Me.y)Return Me.yEnd FunctionPublic Sub NewY()Console.WriteLine("派生类的新方法")End Sub&&“你看清了吧,我们通过声明Inherits关键字,来标识该类的基类是谁,如果没有标识的类,比如CBaseHenry,VB.Net就会视其为派生自Object基类,这是种隐式继承,和结构是隐式继承自ValueType 类的方式是一样的。”大李指着屏幕跟我解说着。“那这些overrides与overload标识是什么意思呀?”我隐隐感觉到它们的意义,却没法清楚地说出来。“我们使用继承,也要允许派生类中的某些属性或方法有与基类不同的行为,我们想‘重写’它,但基类也得要同意你才能重写呀,否则不就会发生命名冲突了吗?因为派生类的对象是可以使用基类的公用成员的,那怎么知道哪些基类的方法与属性是被派生类重写了呢?所以就必须有一个约定:我们在基类中用Overridable修饰符来标识允许基类中的属性或方法在其派生类中被重写,没有标识的其实就是默认为有NotOverridable修饰符来隐式地标识,用于提醒编译器该属性或方法不可被重写;然后在派生类重写时,我们就用Overrides修饰符来标识哪些是重写自基类中定义的 Overridable 属性或方法。”大李娓娓道来,“我们来演练一下对派生类的操作吧。” Main()Dim obj As CDerivedHenry = New CDerivedHenry()obj.GetY(4)&&& '调用派生类的GetY方法obj.OldY()&&&& '调用基类的oldY方法obj.NewY()&&&& '调用派生类的NewY方法&然后大李按了一下F5键,在“输出”窗口中就出现了如下的运行结果:‘ CDerivedHenry()‘‘‘&“你看,你只在Main中实例化了一个派生类的对象,为什么先会出现‘基类的构造’?”大李问。“这个么,”我开始在代码中查看起来,“是这句了。”我手指着派生类里的这段代码:()MyBase.New() ‘Console.WriteLine("派生类的构造")&“没错,我们必须要注意这一点,就是派生类的构造函数与析构函数都必须重新编写。New 构造函数一般用于打开文件、连接到数据库、初始化变量以及处理任何需要在可使用对象前完成的其他任务。我们必须在Sub New 构造函数中的第一行代码使用语句 MyBase.New(),来调用类层次结构中该类的基类的构造函数,以获得基类的性质。析构则是在Sub Finalize中执行完对派生类的的清理任务,如保存状态信息、关闭文件和与数据库的连接,以及执行在释放对象前必须完成的其他任务之后,在析构函数的最后一句使用语句 MyBase.Finalize() 显式调用其基类的 Sub Finalize 方法,以析构MyBase.New()构造的内容。所以你从程序运行结果中也可以很清楚的看出这一顺序。”大李敲击着屏幕,象是给我提个醒。“明白了,我会记着的。”我诚恳地点头回应着。大李突然想起了什么,抬头对我说:“讲到类的继承,我们还得看一下重载与隐藏的问题。”Henry的VB.NET之旅(六)—重载与隐藏&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 韩睿&大李坐在转椅上左右晃动着,手中的一个硬币在他指间灵巧地翻滚着。“hi, Henry。你对重写与重载的意思理解是怎么样的呢?”我正盯着他手中的硬币发愣呢,“哦,重写,就是您刚才举的示例中,在派生类中用Overrides重新编写有Overridable标识的基类的方法或属性;重载么,就是我们可以用同样的名称,在一个类中用不同的参数列表来创建多个方法和属性,在调用时就可以适应不同参数类型的要求。”“在一个类中创建?”大李的左眉向上一挑,我就知道我可能说错了。但是,好象没有说错什么呀。“那好,你看一下,这样的写法会有什么结果?” CBaseHenryPublic Sub oldY()Console.WriteLine("基类的oldY方法")End Sub CDerivedHenryInherits CBaseHenryPublic Overloads Sub oldY(ByVal j As Integer)Console.WriteLine("派生类的oldY方法")End Sub Main()Dim obj As CDerivedHenry = New CDerivedHenry()&然后大李写了obj.oldY这时出现的智能感知菜单上出现了参数列表:&&&&“咦,第二个oldY()无参数过程签名应该是基类定义的呀。为什么在派生类的实例中也会出现呀!”我不由好奇起来。“没错。还记得我上次曾显式编写构造函数的事吗?我用了一个mybase.new()用于继承下基类的性质。现在没有写,其实是一种默认的隐式调用。”大李一说我就明白了,其实现在的派生类应该是内含两个oldY方法了,无参数的oldY()其实就是基类的方法,是由于Overloads和Mybase.new()双重作用的影响。“那你再看这样的变化情况。”大李象是看出我已经明白了这个问题。他在派生类中添加了一个用Overloads标识的与基类形式一致的无参数oldY()。 CBaseHenryPublic Sub oldY()Console.WriteLine("基类的oldY方法")End Sub CDerivedHenryInherits CBaseHenryPublic Overloads Sub oldY(ByVal j As Integer)Console.WriteLine("派生类的oldY方法1")Public Sub oldY()Console.WriteLine("派生类的oldY方法2")End Sub Main()Dim obj As CDerivedHenry = New CDerivedHenry()obj.oldY()&“好的,现在你再说说看,现在的obj.oldY()的运行结果会打印出什么?”大李手按在F5运行键上问我。“应该是派生类的,不对,基类,呃,派生类……”我一下子就晕了。“呵呵。”大李也不禁发笑起来。然后点击了一下F5键。结果是:&&“这就叫隐藏,我们用overloads方式,隐藏了基类的同名方法。以防用户发生混淆。一般来说,隐藏有两种情况,一种是通过范围来实现。比如你定义一个全局变量x,但在一个方法中,你又定义了一个局部变量x,在方法中使用x时,使用的是局部变量的那一个,也就是用局部变量x在方法中隐藏了全局变量x。另一种情况,就是通过继承来隐藏,方法么,除了刚才用的overloads,还可以用Shadows关键字来实现。”“Shadows?阴影?倒是很贴切的名字。这个是怎么用的呢?”我兴趣由然而生。“Shadows功能很强的。”说着,大李又开始修改刚才的代码了。& CBaseHenryPublic Sub oldY()Console.WriteLine("基类的oldY方法")End Sub CDerivedHenryInherits CBaseHenryPublic Shadows Sub oldY(ByVal j As Integer)Console.WriteLine("派生类的oldY方法")End Sub Main()&Dim obj As CDerivedHenry = New CDerivedHenry()&再写到obj.oldY时,出现的智能感知菜单上就剩下一个方法了。& &“哈,真的呢,基类的方法看不到了。但是,这和重写的效果岂不是一样了吗?”我不由地又想起一个问题,如果是一样的作用,要Shadows干什么呀。“还是有区别的。”大李开始扳着指头数起来,“最明显的区别在于,隐藏适用于任何元素类型,你可以在派生类中用Public Shadows oldY as Integer来隐藏基类的oldY()方法;而重写只能适用于方法与属性,而且声明参数与修饰符都要求完全一致。”“还要注意一点,当派生类又派生出一个子类时,重写与隐藏都会被继承下去。当然,如果在派生类中是用private来修饰Shadows成员的话,它的子类就会继承它基类的成员了。”“我倒是感觉,现在VB.NET对继承的处理功能真的很强大,有继承,对于编程者来说,真的是件好事。”我不由感叹道。“不错,继承层次体系是有很多好处,但是事物总是有其双面性,继承也有不少问题,其中最麻烦的,就是‘脆弱的基类’。”大李紧锁眉头认真地说。“哦,什么叫‘脆弱的基类’?”我不解地问。Henry的VB.NET之旅(七)—脆弱的基类&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 韩睿&“既然说是脆弱,当然是指它象蛋壳一样不堪一击喽。这个问题其实很好理解。程序总是由人来设计与编写的,所以工作开始时考虑不到某些问题当然也是很正常的事。所以可能在工作进行了一段时间后发现基类需要变更。你想,如果我在基类中更改了成员的数据类型,以及那些允许重写的那些方法和属性,那派生类及其子类还能正常工作吗?尤其是当一个团队中的多个开发人员一起来创作基类和派生类时,就更是要命了。很多情况下,大家可能已经把基类和一些派生类编译成二进制形式进行提交了。更改基类,重新编译再分布,会牵一发而动全身,导致项目的崩溃。所以我们把这叫做‘脆弱的基类’。也就是说,它是整个工程中最薄弱最致命的环节。”大李眉头一直紧锁着,想必是回想起了自己受打击的经历。“这么严重呀,现在的软件工程设计方法会不会对这个有很好的解决方案?”我努力想缓解一下大李的严肃神情。“如果对项目的前期设计考虑尽可能周详,在工程实施中对项目的代码控制与相关性分析做得踏实,会起到很好的效果。但是不管一个人如何努力,有时还是无法避免对基类进行不可预见的更改。我们摸索过很久,有了一些处理的手段。”“真是成事在人呀,我们现在有什么解决之道?”我也一下子振奋起来了。“呵呵,并不是什么完美解决方案。只能在某种程度上减轻危害。我们常用的一个方法,最直接的思想就是,把有可能发生的更改全都放在派生类中进行,不在基类中做。”“这具体是什么意思呀,我还是不太明白。”我不好意思地挠挠头。大李微笑着点点头,看来是知道我不会明白的了。“我们在基类中使用的是抽象类,它内含的方法与属性只有定义,没有进行实现,而把实现部分都放在派生类中做。这样一来,抽象类自身是无法被实例化的。但是它的好处不言而喻,就是有可能发生的实现上的更改都会只涉及到它的派生类了。VB.NET中就提供了这样的手段。”说着,大李就打开VS.NET集成编译环境,顺手写了一小段代码: Class CBaseHenryPublic MustOverride Sub subX(ByVal x As Integer)Public MustOverride Function fcnY(ByVal y As Integer) As Long CDerivedHenryInherits CBaseHenryPublic Overrides Sub subX(ByVal x As Integer)&& Public Overrides Function fcnY(ByVal y As Integer) As LongEnd Class&“这里要注意两个问题,一个是关键字,我们用MustInherit来修饰类名,使类成为抽象类,在它的成员中,把方法和属性前加入MustOverride修饰符表示它们必须在派生类中加以实现。第二个要注意的是,派生类必须对所有用MustOverride标识的基类方法和属性都进行实现,只重写了subX,不写fcnY编译器会报错的。”“这的确可以解决一部分问题,但好象只能解决在基类中进行实现的代码有更改的问题,对于数据类型的更改好象没有什么效果。”我看了好一会,发出了这样的疑问。“所以我刚才说,是在某种程度上进行解决嘛。”大李也不由笑了起来,“不过你提的这个问题,倒不是太麻烦,我们可以在派生类中用Shadows来解决呀!(详见本报上一期《重载与隐藏》)”这倒是个不错的主意,我心中暗暗评价了一番。突然我又想到一个问题:“如果基类要做功能扩展,怎么办呀?”“如果是要做扩展,最安全的方法是添加新成员,而不是对基类的大肆修改。一般是往派生类添加设计时缺失的新成员。最好不要使用Overloads关键字来命名与基类相同的成员,那样往往会带给你意想不到的问题。最好重新定义新成员,命名上也要尽量与基类已有的成员名区分开来。其实,也可以往抽象类基类中添加新成员的定义,但这样一来,需要为基类制定版本,虽然不会对应用程序造成毁灭性的危害,但是应该要能够完全地控制与管理自己的代码。我们一般是不希望扩展基类的。”我已经大意上领会了大李的一片苦心:“您的意思,是不是指基类的脆弱问题实际上是客观存在的,我们所做的就是要最大程度的减小这个问题带来的危害?”大李眼中闪过一丝赞许的笑意,颌首道:“没错,对于一个应用程序的设计者来讲,想使用面向对象方法来开发,必须要在设计的时候精心策划类的层次结构。一般来说,是有这样几个准则需要把握的:第一,遵循先通用,再专用的原则。先设计好层次结构中每一级别的类中的通用部分,也就是提供给派生类继承的成员和标识为Public的成员;第二,在定义数据类型和存储区时要有预留量,以避免以后更改困难。例如,即使当前数据可能仅需要Integer类型就够了,在设计时我们使用 Long 类型的变量。当然,最好能物尽其用,也不要盲目放大;第三,在一个项目中,必须统一管理与分配团队中使用的所有的命名,以减少命名冲突,这一点其实事关重大;第四,要使用提供可行的最低访问权限的访问修饰符声明类成员。内部类成员应声明为 Private;仅在类内与派生类才需要的成员应标记为Protected;Friend 数据成员可以从类的外部访问,但仅限于该模块是定义该类的项目的一个组成部分;使用Public标识的成员,只能是实例化时真正需要的内容,而且经常用在类层次结构的底部。”“也就是说,一个规范的操作,标准的命名体系可以决定基类的强壮与否?”我不禁感触了一声。“不对,应该这样说,可以决定的是给脆弱的基类穿上多厚的防护衣。因为基类始终都是脆弱的。”大李更正道。我连声赞同:“对,对。我现在是真正明白为什么总有人提编程规范的事情,我一直认为是增强代码的可读性,没想到,对程序自身还有这么大的帮助。”“当然,其实你认真想一下,Overrides关键字的作用,不管要不要注明,编译器都可以很方便地判断方法或属性是否在基类中,签名是否匹配,但是VB.NET要求我们必须标注,就是强制开发人员注明重载基类方法或属性的意图,使开发过程更合理与有效。此外,还有更重要的就是,我们要在工程实践中不断地学习与磨练,了解更多的知识,获得更多的经验,这样才会成长为一名合格的程序设计师。就拿继承来说吧,在.NET中其实支持三种继承方式:实现继承、接口继承、可视继承。我们其实只用了第一种继承方式,你看,要学的东西是不是很多?”大李友好地拍了拍我的肩膀。Henry的VB.NET之旅(八)—接口&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 韩睿&“还有两种继承方法?”我是真正被VB.NET的强大功能所折服了。求知的渴望驱使着我向大李露出了一个最灿烂的笑容,“行了,别傻笑了,我告诉你不就成了。”大李不禁也笑了起来。“刚才我说到‘脆弱的基类’时,就提到实现继承最大的问题,就在于基类与派生类之间的关系过于紧密,还记得吧?基类实现细节往往会泄露出来,这不是我们愿意看到的封装情况,所以有很多程序设计师就想尽方法来改进这一问题,其中最出名的,就是COM了。”“COM?我在VB6中经常用呀,是一种组件形式呀。”我不是太明白大李的话,这和面向对象的继承有什么关系?“你说的不错,正是通过组件的这种封装方式,我们就可以把实现继承局限于在组件内部使用,而我们使用组件时,就不用理会它内部是什么,怎么实现的。这就可以避免不可预测地对基类的修改。我们把利用组件的组织程序方法称为面向组件程序设计,但这也是一种面向对象的设计方法,不过是更具强制性。组件支持组件内部的实现继承,还支持接口继承。”“接口继承,我不是太清楚。不过,接口我是清楚的,就是组件开放的属性、方法与事件、公用变量的定义方法。我在VB6中也接触过接口编程,不是太方便,好象要把定义了方法却没有写实现过程的类编译成DLL文件,VB6会自动将它创建为接口,不过只能是隐藏的,不是显式定义的。”我回忆了一下说。大李微微点了点头说:“从面向对象的观点来看,接口就是对象的外观,而对象实际的工作方式就是实现。把接口与实现分离开就是我们要进行封装的动机。用户只能通过接口来操作,但是看不到具体的实现的代码。”大李顿了一顿,然后接着说:“VB.NET 以前的 Visual Basic 版本可以使用接口,但不能直接创建它们。VB.NET 却是允许可以用 Interface 语句定义真正的接口的喔!”此言一出,真让我大吃一惊。“我们也可以直接定义接口吗?”“当然,”大李说,“在VB.NET中,和类一样,接口也可以定义属性、方法和事件。但正如我刚才说到的,与类不同的是,接口并不提供实现。现在的接口,是由类来实现的,并从类中被定义为单独的实体。”大李手指在桌面上重重的敲了一下,加强了一下语气:“我们可以这样来理解,接口表示的是一种约定。实现接口的类必须严格按其定义来实现接口的每个方面。有了接口,就可以将功能定义为一些紧密相关成员的小组。可以在不危害现有代码的情况下,开发接口的增强型实现,从而使兼容性问题最小化。也可以在任何时候通过开发附加接口和实现来添加新的功能。虽然接口实现可以进化,但接口本身一旦被发布就不能再更改。对已发布的接口进行更改会破坏现有的代码。若把接口视为约定,很明显约定双方都各有其承担的义务。接口的发布者同意不再更改该接口,接口的实现者则同意严格按设计来实现接口。”“也就是说,在VB.NET中,接口是用类来实现的,就象是个抽象类,只是关键字用的是Interface,不是class,对吗?”我还是很好奇。“接口的实现可以是类,也可以是结构。接口的定义用的是Interface关键字,实现时用的是Implements关键字”大李淡淡的一句话,使我在心中开始回忆起类和结构的差别来(详见前文《类和结构》)。大李接着跟我解说:“接口的成员包括其成员声明引入的成员和从其基接口继承的成员。只有嵌套类型、方法、属性和事件才能作为接口成员。方法和属性不能有实体。接口成员隐式地定为是Public,而且不能指定访问修饰符。接口自已倒是可以添加修饰符。”大李跟着看了我一眼,就在电脑上开始写起示例来: IHenrySub subX(ByVal x As Integer)Function fcnY(ByVal y As Integer) As LongProperty proZ() As String CHenryImplements IHenryPrivate z1 As StringSub subX(ByVal x As Integer) Implements IHenry.subXFunction fcnY(ByVal y As Integer) As Long Implements IHenry.fcnY&&&&Property proZ() As String Implements IHenry.proZGetReturn z1End GetSet(ByVal Value As String)z1 = ValueEnd SetEnd Property&&大李指着代码说:“你看,我用Interface定义了一个接口IHenry(笔者注:一般来说,接口的命名第一个字母为I,这并没有什么强制的含义,但却是一个通用的命名规则),内含三个方法与属性的定义,但并没有实现;实现代码写在CHenry类中。你可以按我刚才说的约定的思路来理解,IHenry接口其实就是一个合约的提纲,CHenry是该合约的一个操作版本,只要在CHenry中实现了接口IHenry定义的所有的方法,不管是怎么实现的,有没有加入新的方法,都可认为CHenry是支持IHenry接口的一个实现类。”“一个实现类?也就是说接口可以有多个实现喽?”我不解地问。“当然,我刚才说过,接口其实就是一个对象的外观,在VB.NET中有很多很重要的接口,定义了很多种类型的对象,比如说你所熟悉的Windows Form的控件,它们的基类大部分是Component类,而Component类就是IComponent接口的一个实现类,IComponent类还有其它三个实现类,那就是:Control、HttpApplication和MarshalByValueComponent类,分别完成不同的功能,但它们都要实现IComponent接口所定义的方法与属性,且参数定义与返回类型都要与接口定义时的签名一致。换个角度来看这个问题,我们如何创建一个组件,且让系统都识别,靠的就是对接口的实现。要成为组件的类必须实现 IComponent 接口,并提供不需要参数或只需一个类型为 IContainer 的参数的基本构造函数就行了。”“哦,我明白了,通过接口,我们可以定义下某种对象的基本外观,然后可以自由地进行实现与扩展,却不涉及对原型的直接修改。太棒了!”我一下子高兴了起来。“是呀,在VB.NET中可以显式定义接口,使得接口编程也成为很棒的编程方式了。刚才所说的,在不同的类中实现同一个接口,不就是一种用接口实现的多态性吗?另外,在类和结构中也可以实现多个接口。”大李写了如下的代码给我看:&Sub SubOne()Sub SubTwo()Implements IOneImplements ITwoSub SubOne() Implements IOne.SubOne&&&&'实现代码End SubSub SubTwo() Implements ITwo.SubTwo'实现代码End SubEnd Class&“真有意思,”我饶有兴致地看着代码,“也就是说CHenry就具备了IOne与ITwo所定义的外观特点了。”我一下子想起了抽象类的定义,不由好奇地说:“如果把接口看成一个基类,那么用于实现的类不就可以看成是它的一个派生类了?这是不是就是接口继承呀?”Henry的VB.NET之旅(九)—接口继承&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 韩睿&大李拍了拍我的肩膀说:“你真有想象力,不过的确,有很多文献把这种用Implements来实现接口的方法就称为接口继承。其实,接口自己也是可以进行继承的,在VB.NET中把接口间的继承形式称为接口继承。”我不禁跟着笑了起来:“接口继承要成为继承,当然要用Inherits,对吧?”大李点点头说:“既然你都清楚了,那你来模拟一个下拉框Combobox的接口吧。”“Combobox?”我不禁一愣,不过一会就想明白了,“是不是要让它符合有文本框供文字输入与下拉列表供选择列表项的组合形式这样的外观?”大李跟着提醒了我一句:“接口与VB.NET中的类继承还是有不同的,它可以支持从多个接口进行多重继承,VB.NET中的类只支持单一基类的继承。”见大李没什么别的意见,我就开始写起代码来:&Sub Paint()‘Sub SetText(ByVal text As String)‘Sub SetItems(ByVal items() As String)Inherits ITextBox, IListBoxImplements IComboBoxSub SetText(ByVal text As String) Implements ITextBox.SetText'实现代码End SubSub SetItems(ByVal items() As String) Implements IListBox.SetItems'实现代码End Sub……&写到这,发现CHenry类中的Implements IcomboBox的ICombobox下面还有一条波浪线,说明接口并没有实现完毕,可是我已经把IComboBox继承的两个基接口中的方法都已经实现了呀。把鼠标靠近波浪线一看,系统提示“必须为接口IControl实现sub Paint()”,于是我就继续写: IControl.Paint'实现代码End Sub&&我转回头问大李:“接口的实现类中是不是要把接口的所有基接口都要实现一遍呀?” ITextbox.Paint IListBox.Paint&Sub Paint()‘ text As String)‘Sub SetItems(ByVal items() As String)Inherits ITextBox, IListBox x As IComboBox)x.Paint()&方法或属性名倒不用与接口中定义的名字一样,只要参数列表与返回类型一致就行了。比如在CHenry中的sub Paint()如果更名为sub xxx()也是可以的,只要后面跟着Implements IControl.Paint就行了。命名一定要有规划,不然,接口继承中也会带来命名重复造成的问题,我们来看一下。”&Property yyy() As IntegerSub yyy(ByVal i As Integer)Inherits IHenry1Inherits IHenry2x As IHenryDerived)x.yyy(1)x.yyy = 10End Sub&“你看,在sub test()中,无论你按IHenry2中的定义方式来使用x.yyy(1),还是用IHenry1中的方式来使用x.yyy=10,集成编译器都会在它们下方打上波浪线,表示出错,是什么错呢?”大李一边问我,一边把鼠标靠近波浪线,出现了编译器的出错提示:“yyy”在继承接口“IHenry1”与“IHenry2”之间不明确“所以,我一直强调命名规则,对吧?”大李看了我一眼,“其实解决方法倒用不着去更改基接口中的方法与属性名。”Dim x As IHenryDerivedCType(x, IHenry1).yyy = 10CType(x, IHenry2).yyy(1)End Sub&“哦,用强制类型转换就可以了。”我又学到一招,不禁暗自窃喜。但是我心里总是有一个不大不小的疙瘩,说来说去,这接口与抽象类可真的太象了。赶紧得问问:“大李哥,这接口与……”“抽象类?”大李一口就接了上来:“别急,小伙子,看看几点了,该下楼吃午饭了。”Henry的VB.NET之旅(十)—何时用接口&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 韩睿&大李没告诉我接口与抽象类的区别,什么时候用接口,什么时候用实现继承。弄得我中饭也没吃好,老在琢磨这事,这不,一吃完饭,我就冲上楼,一个房间一个房间转,到处找大李。过了好一阵,这老哥才和几个同事说说笑笑地回到办公室,我立即走上前,半请半拉地把他拽到电脑旁。“大李哥,我实在想得头晕,既然在VB.NET中接口有了这么大的发展空间,在形式上与抽象类如此相似,那么它们有什么区别?什么时候用接口呢?”听着我一连串的问题,大李微笑着摇摇头,拍拍我的肩膀说:“小兄弟,不光是你弄不清,其实就是很有经验的程序设计师也对什么时候用接口,什么时候用抽象类而头痛咧。”此话一出,我更是疑惑重重。不过反而安下心来,老鸟们都弄不清的问题,我不清楚也不必心中不安了。哈……大李看着我忽忧忽喜的表情露出了一丝诧异,不过他没有理会,继续说:“但是这个问题我们还是有必要好好分析一下,让我们更明白接口与抽象类的具体含义。”“我们早说过,抽象类是一种不能实例化而必须从中继承的类。抽象类可以完全实现,但更常见的是部分实现或者根本不实现,从而封装继承类的通用功能,它可以提供已实现的成员,因此,可以用抽象类确保特定数量的相同功能,但不能用接口这样做。”“接口是完全抽象的成员集合,它的成员都无法在接口定义时实现,我们可以将它看作是为操作定义合同,接口的实现完全留给开发者去做。它们之间的区别,如果认真分析,还是有不少的:在VB.NET中,类只能是从一个基类继承,所以如果要使用抽象类为一组类提供多态性,这些类必须都是从那个类继承的;接口就不一样了,它不但可以用一个类或结构实现多个接口,一个接口还可以有多个实现。”“也就是说,它们在提供多态性的问题上是有差别的?”我好象听懂了点什么。“这是一个重要的区别,我们也可以从多态性的角度来考虑是要使用接口还是用抽象类。”大李同意了我的观点,“如果预计要创建组件的多个版本,我们应该创建抽象类。这是因为,抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。这是好处,当然也是问题,对吧?(详见前文《脆弱的基类》)另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。所以,如果创建的功能将在大范围的全异对象间使用,则使用接口。”我想了一下,接着大李的话说:“能不能这样说,抽象类主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。”大李对我伸出了大拇指:“不错,小伙子悟性很好呀!你想,我上午跟你说,要创建控件,首先就是要对一些接口进行实现以让系统能够识别(详见前文《接口》)。而各个控件之间的联系其实关联性并不大。所以,它们的基础大都是接口。但是,我们要注意一点,在组件设计时,如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。这是因为我们刚才说过的原因,抽象类允许部分实现类,而接口不包含任何成员的实现。”“唔,明白了,它们之间的区别有点明白了。”我默默地点了点头。“另外,有个通用的设计思想,如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。”大李又补充了一条建议。  “看来设计的问题还是蛮大的,一般来说,怎么设计接口呢?”我接着问。“为什么你所看到的编程书籍也好,程序例程也好,极少有对接口的描述,而对类实现继承的例子比比皆是?这就从一个侧面给我们提了一个醒,如果使用适当,接口可以成为很有用的工具。但如果使用不当,它们会变得非常棘手,甚至妨碍有效的编程。接口的设计与使用其实是一项高明的艺术。”大李郑重其事的说。“艺术?”我惊叫了一声。“没错,艺术!”大李又加重了一下语气,““但是,最大的问题还是集中在接口设计上。”大李接着说,“接口一旦被定义和接受,就必须保持不变,以保护为使用该接口而编写的应用程序。接口发布后,就不能对其进行更改。这是我们进行组件设计的一个重要原则,叫做‘接口不变性’。”我点了点头:“接口不变性,这个我可以理解了。”“我已经反反复复强调过,创建一个接口就是在创建一个定义,接口定义发布后则永远不能更改。接口不变性,就是为了保护为使用接口而编写的现有系统。当接口设计与需求有所出入,确认需要大幅度变更时,我们应该创建新的接口。一般命名方式是,新接口可以通过在原来的接口名称后追加一个数字‘2’来命名,以显示出它与现有接口的关系。然后通过接口继承来创建新接口。”  “可是,如果需求分析得不好,岂不是会出现一大堆的派生接口了?”我不免有点顾虑。“这是肯定的,而且过于频繁地生成新接口,会因未使用的接口实现而使组件变得很庞大。有经验的设计师,在充分分析需求后,设计出的接口往往很小且相互独立,减少了性能问题发生的可能。”“这种分解能力倒真的是艺术呀!”我不禁为之叹服。“当然,一般来说,我们会把确定哪些属性和方法属于接口的设计过程称为‘接口分解’。基本准则是,应在一个接口中将紧密相关的几个功能聚合为一组。功能太多会使接口不便于运行,而过于细分功能将导致额外的系统开销并降低使用的简易性。掌握分解的这个度的能力是需要不断的在技术上进行磨炼,以及在对每个项目深入分析与理解的基础上才能得到的。”“明白了。”我大声地回答着,真希望自己能早一天成为接口设计大师。大李笑着拍了拍我:“明白了就好。其实,与设计接口相比,创建大的实现继承树更容易出错。”“当然,”我脑海里浮现起实现继承的各个环节,“这是不是说,在某些时候适当使用接口还是很有益的。”“看来你真的明白了,那你再来说一下,接口与类实现继承相比,好处有什么?”大李回过身开始找茶杯。我低下头,努力地转动了一下脑子:“我试着说一下吧,总体而言,接口是一种非常有效的编程方法,它让对象的定义与实现分离,从而可以在不破坏现有应用程序的情况下使对象得以完善与进化。接口消除了实现继承的一个大问题,就是在对设计实施后再对其进行更改时很可能对代码产生破坏。即使实现继承允许类从基类继承实现,在类首次发布时仍然会使我们不得不为设计做很多的抉择。如果原有的设想不正确,并非总可以在以后的版本中对代码进行安全的更改。比如,我们定义了一个基类的方法,它需要一个 Integer 参数,而后来又确定该参数应该为 Long 数据类型。我们无法安全更改原始类,因为为从原始类派生的类所设计的应用程序可能无法进行正确编译。这一问题会扩大化,因为单个基类会影响几百个子类。”“那用重载原始类并采用一个Long类型的参数,不就能解决这个问题了吗?”大李提了个问题。 Long 使用接口继承而不用类继承的主要原因有:在应用程序要求很多可能不相关的对象类型以提供某种功能的情况下,用接口适用性会更强;接口比基类更灵活,因为可以定义单个实现来实现多个接口;在无需从基类继承实现的情况下,接口更好;在无法使用类继承的情况下接口是很有用的。例如,结构无法从类继承,但它们可以实现接口。”我抿着嘴用力点了点头,同时在心里默默地记忆着大李所说的每一条准则。VB.NETHenry的VB.NET之旅(十一)—可视继承&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 韩睿&VB.NETVB.NETVB.NETVB.NETInheritsVB.NET“”“”“”“”
Windows Windows Windows btnProtected Text
btnProtected
btnProtected_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles btnProtected.ClickMsgBox("protected的按钮")&btnPrivate Text
btnPrivate
btnPrivate_Click(ByVal sender As System.Object, _ System.EventArgs) Handles btnPrivate.ClickMsgBox("private的按钮")&bin.dll Windows
Form1窗体,命名为“InheritanceForm”。随即在Windows 窗体设计器中,派生窗体的继承而来的按钮左上角会带有一个箭头标志符号PrivatePrivate btnProtected_Click(ByVal sender As System.Object, _ System.EventArgs) Handles btnProtected.Click&
“”
InheritanceForm
F5 Protectedclickclickprivate subprivate Sub btnProtected_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnProtected.ClickMsgBox("protected的控件")&InheritanceFormoverrides&btnProtected_Click Sub btnProtected_Click(ByVal sender As Object, _ System.EventArgs)&HenryVB.NET—&VB.NETVB btnProtected_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnProtected.Click&&&VBSendereHandlesWindows注:此处的Windows是指Win9X及其以上的操作系统WindowsWindowsVBWindowswindow procedurewindowsButton1clickButton1_ClickSenderSenderobjectsender as objectobject .NET
.NET sendersendersenderVB.NETeeEventargsMousedownmouseeSystem.Windows.Forms.MouseEventArgseButton1MosueDownButton1.MouseDown Button1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Button1.MouseDown&eSystem.Windows.Forms.MouseEventArgse1&button1.clickbutton1.Mosuedowne1ButtonClicksDeltaX x Y y &&&&&&&&&&&&&&&&& &&&&&&&& 1 eHandlesHandles Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _Handles Button1.Click, Button2.ClickSelect Case sender.nameCase "Button1"MsgBox("你点击的是button1")Case "Button2"&&MsgBox("你点击的是button2")End Select&&HandlesSendereHenryVB.NET—&RegionWindows
Button1 As System.Windows.Forms.Button&Friend WithEventWithEvents WithEvents Handles
WithEvents WithEvent-HandlesCHenryEventHR&Public Class CHenryPublic Event EventHR()&&& ' 声明一个事件Sub CauseSomeEvent()RaiseEvent EventHR()&& ' 引发事件End SubEnd ClassWithEvents Obj As New CHenry()&& '模块或类级别的声明Sub Obj_EventHR() Handles Obj.EventHR '在Handles之后声明事件MsgBox("事件处理器捕捉到了事件.")&& '处理事件.End SubSub Main()Obj.CauseSomeEvent()&& '调用对象去引发事件End Sub&RaiseEvent_VB.NET&Public Class CHenryPublic Event EventHR()&&& ' 声明一个事件Sub CauseSomeEvent()RaiseEvent EventHR()&& ' 引发事件End SubEnd ClassPublic Class Class2  ‘Inherits CHenrySub Obj_EventHR() Handles MyBase.EventHR'&&End ClassWithEvents Obj As New Class2()&& '模块或类级别的声明Sub Main()Obj.CauseSomeEvent()&& '调用对象去引发事件End Sub&MyBase Handles MyBase.&event name& objClass2ChenryHenryVB.NET—&啊,听到这,Henry突然明白了事件处理程序中的第一个参量为什么叫Sender了,就是指事件发送的那个对象呀.NET DelegateVisual Basic.NET
System.Delegate .NET& CHenryPublic Event EventHR()&&& ' 声明一个事件Sub CauseSomeEvent()RaiseEvent EventHR()&& ' 引发事件End SubEnd ClassDim obj As New CHenry()Sub Obj_EventHR()& '在Handles之后声明事件MsgBox("事件处理器捕捉到了事件.")&& '处理事件.End SubSub Main()AddHandler obj.EventHR, AddressOf Obj_EventHRobj.CauseSomeEvent()&& '调用对象去引发事件&objWithEventsobj_EventHR()HandlesEventHRWithEvent-HandlesAddHandleAddressofAddressofAddressOf AddressOfAddHandleobj.EventHRObj_EventHRRemoveHandlerAddHandle Obj_EventHRAddHandler
RemoveHandler
Handles AddHandler AddressOf& TextBoxMouseDownHandler TextBoxMouseDownHandler( )‘ TextBoxMouseDownHandler(ByVal sender As Object, ByVal e As EventArgs) TextBoxMouseDownHandler(ByVal sender As Object, ByVal e As MouseEventArgs)&&AddHandlerAddressOfMouseDownMouseEventhandlerobjectSystem.Windows.Forms.MouseEventArgsAddHandlerRemoveHandlerMouseEventhandler&& System.EventArgs MouseDownMouseEventHandlerLabel1MouseDown& Label1 As System.Windows.Forms.Label Label1_MouseDown(ByVal sender As Object, ByVal e As _ Label1.MouseDown&& Label1_MouseDown Label1_MouseDown(ByVal sender As Object, ByVal e As _&MouseEventHandlerMouseDownOnEventHROnEventHR&Public Class HenryEventArgsInherits EventArgsEnd Class'声明委托Public Delegate Sub HenryEventHandler(ByVal sender As Object, _      & ByVal e As HenryEventArgs)Public Class CHenry' 声明一个事件Public Event EventHR As HenryEventHandlerProtected Overridable Sub OnEventHR(ByVal e As HenryEventArgs)'调用委托RaiseEvent EventHR(Me, e)End SubPublic Sub start()Dim e As HenryEventArgsOnEventHR(e)End SubEnd ClassDim obj As New CHenry()Sub obj_EventHR(ByVal sender As Object, ByVal e As HenryEventArgs)'Sub Main()AddHandler obj.EventHR, AddressOf obj_EventHRobj.start()&&EventHREventHRSenderHenryEventArgsOnEventHROnEventHRCHenryVB.NET!&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
[商业源码]&
Copyright &
All Rights Reserved

我要回帖

更多关于 真是奇怪le优酷 的文章

 

随机推荐