国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
2.2 VB.NET語法基礎(chǔ)
正如語言是人們之間交流的基礎(chǔ),要想開發(fā)軟件,就必須掌握一種計(jì)算機(jī)編程語言。在前面的章節(jié)中已經(jīng)編寫了一些簡單的程序,但要想真正地開發(fā)出界面美觀、功能完善的軟件,就必須更進(jìn)一步地學(xué)習(xí)計(jì)算機(jī)編程語言。
本節(jié)介紹VB.NET的基礎(chǔ)語法。
2.2.1 基本數(shù)據(jù)類型
我們從小學(xué)就開始學(xué)習(xí)數(shù)學(xué),知道有整數(shù)、小數(shù)、有理數(shù)、無理數(shù)、實(shí)數(shù)和虛數(shù)等許多數(shù)的類型。但在大多數(shù)計(jì)算機(jī)編程語言中,并沒有這么多種數(shù)的類型,基本的數(shù)只有兩種:整數(shù)和小數(shù)。但根據(jù)數(shù)表示范圍的不同,這兩大類又可以再進(jìn)一步細(xì)分。
在VB.NET中,數(shù)字分為如表2-5所示的幾種類型。
表2-5 VB.NET數(shù)字類型
VB類型
CLR類型
存儲(chǔ)空間
取值范圍
Byte
System.Byte
1 個(gè)字節(jié)
0到255(無符號)
Decimal
System.Decimal
16 個(gè)字節(jié)
0到+/-79 228 162 514 264 337 593 543 950 335之間的整數(shù)
0到+/-7.922 816 251 426 433 759 354 395 033 5之間的小數(shù)
Double
System.Double
8 個(gè)字節(jié)
負(fù)值取值范圍為-1.797 693 134 862 315 70E+308到-4.940 656 458 412 465 44E-324
正值取值范圍為4.940 656 458 412 465 44E-324到1.797 693 134 862 315 70E+308
Integer
System.Int32
4 個(gè)字節(jié)
-2 147 483 648到2 147 483 647
Long
System.Int64
8 個(gè)字節(jié)
-9 223 372 036 854 775 808到9 223 372 036 854 775 807
Short
System.Int16
2 個(gè)字節(jié)
-32 768到32 767
Single
System.Single
4 個(gè)字節(jié)
負(fù)值取值范圍為-3.402 823 5E+38到-1.401 298E-45
正值取值范圍為1.401 298E-45到3.402 823 5E+38
表2-5中的第一列是VB.NET中提供的數(shù)據(jù)類型,第二列是.NET Framework中的公共語言運(yùn)行時(shí)(Common Language Runtime,CLR)所提供的數(shù)據(jù)類型。在使用VB.NET編程時(shí),可以同時(shí)使用這兩種數(shù)據(jù)類型。
 技術(shù)內(nèi)幕
事實(shí)上,每種語言都有自己的數(shù)據(jù)類型,比如VB.NET中的Integer,在C#中對應(yīng)的數(shù)據(jù)類型為int。但不管是VB.NET還是C#,或是另外一種編程語言,只要這個(gè)語言編出來的程序是運(yùn)行于.NET之上的,則都要被轉(zhuǎn)換為NET Framework中的公共語言運(yùn)行時(shí)所提供的數(shù)據(jù)類型。正是由于有這個(gè)特點(diǎn),才使得.NET Framework支持混合語言開發(fā),即VB.NET代碼可以調(diào)用C#代碼,這在以前的軟件運(yùn)行平臺(tái)上是很難實(shí)現(xiàn)的。
在這些數(shù)據(jù)類型中,用得最多的是整數(shù)(Integer和Long)和小數(shù)(Double和Single),小的整數(shù)用Integer,大的整數(shù)用Long。小的小數(shù)用Single,值很大的小數(shù)用Double。
另一種非常重要的類型是String,它代表由字符組成的字符串。在前面的程序中已看到過它的身影,由于其特殊的重要性,將在2.2.6節(jié)中專門介紹其使用方法。
知道了VB.NET擁有的數(shù)據(jù)類型,那么這些數(shù)據(jù)類型有什么用?它們是怎樣用在編程中的?
可以用一句話概括為:
數(shù)據(jù)類型在編程中主要用于定義常量和變量。
解釋一下:
在程序中會(huì)用到許許多多的數(shù)據(jù),每種數(shù)據(jù)一定屬于某種數(shù)據(jù)類型。打個(gè)比方,一周有7天,那么,這個(gè)“7”就是一個(gè)數(shù)據(jù),它的類型是Integer(整型)。在軟件中,數(shù)據(jù)本身用常量和變量來表達(dá),由此可以得出一個(gè)結(jié)論:
變量和常量一定屬于一種數(shù)據(jù)類型。
下面介紹變量和常量的知識。
1.常量
所謂常量,就是在程序運(yùn)行過程中始終不變的量。比如,公歷一年有12個(gè)月,這個(gè)概念可以用以下的定義來表達(dá):
Public Const MonthsPerYear As Integer = 12
一旦這樣定義了之后,就可以在代碼中使用MonthsPerYear這個(gè)標(biāo)識符來代替12這個(gè)數(shù)字。把上述信息用標(biāo)簽控件(假設(shè)其為Label1)表達(dá),可以這樣寫代碼:
Label1.Text= "公歷一年有" + cstr(MonthsPerYear) + "個(gè)月"
這句完全等同于:
Label1.Text= "公歷一年有" + cstr(12) + "個(gè)月"
要定義一個(gè)常量,應(yīng)按照以下格式:
Public Const 常量名 As 數(shù)據(jù)類型=值
在上面的格式中,數(shù)據(jù)類型可以是.NET支持的所有數(shù)據(jù)類型。下面是一個(gè)字符串類型常量的例子:
Public Const ErrorMsg As String= "出錯(cuò)了!"
 試一試
請定義一個(gè)常量來表示“中國有56個(gè)民族”。
 注意
在程序中用常量代替數(shù)字和字符串,是個(gè)好習(xí)慣。
為什么要這么做?想想就明白了:
假如程序中在許多地方都要用到一個(gè)特殊意義的數(shù)字,比如999,那么,在需要使用這個(gè)特殊意義的場合使用一個(gè)單詞(比如MaxNumber)來表達(dá)無疑是更易于閱讀與理解的。
比對一下以下兩段代碼:
(1)
Private const MaxNumber As Integer=999
'……
If i>MaxNumber then
Msgbox "數(shù)據(jù)太大了!"
End if
(2)
If i>999 then
Msgbox "數(shù)據(jù)太大了!"
End if
哪個(gè)更易懂?
再想想下面這個(gè)情況:某個(gè)程序中多次需要將一個(gè)數(shù)與最大的三位數(shù)999進(jìn)行比較,因此在某個(gè)程序代碼中“999”這個(gè)數(shù)字出現(xiàn)了10次。現(xiàn)在,程序版本需要升級,可以支持四位數(shù)字的比較,因而原有代碼中的“999”就必須修改為“9999”,必須修改10處代碼。在修改過程中還可能發(fā)現(xiàn)代碼中出現(xiàn)的某個(gè)“999”只是一個(gè)普通的數(shù)字999,并不具備“最大的三位數(shù)”這個(gè)含義,不應(yīng)被改為“9999”。這樣一來,修改代碼工作就很麻煩了,必須仔細(xì)地閱讀其前后的代碼,才能確定是否應(yīng)該將此“999”改為“9999”。
而如果在需要表達(dá)“最大的數(shù)字”這個(gè)含義的地方不用具體的數(shù)字,而用一個(gè)常量MaxNumber來表達(dá)(即按第1種方式編程),則只需要修改常量的定義就行了,上面出現(xiàn)的問題全部都可以避免。
字符串常量是用雙引號定界的,那么,如果需要在字符串中包含雙引號,應(yīng)怎樣處理?
例如,要把以下這句話:
He said "Good Morning!"
定義為字符串常量,必須這樣做:
Public const WhatHeSaid as String=" He said "" Good Morning! "" "
注意需要把雙引號連續(xù)寫兩次。
2.變量
在使用計(jì)算機(jī)語言編程時(shí),經(jīng)常需要定義變量。所謂變量,可以這樣理解,它就是一個(gè)存放信息的容器,容器的大小和類型是固定的,而容器中存放的具體東西可以不同。
例如,以下代碼定義了一個(gè)整型變量:
Dim i as Integer
這表明i是一個(gè)可用于存放整數(shù)的容器,可以把任何一個(gè)整數(shù)放到容器中。如:
i=100
上述代碼意味著這個(gè)“整數(shù)瓶子”中裝的數(shù)是100,再來一句:
i=200
這意味著這個(gè)“整數(shù)瓶子”現(xiàn)在裝的數(shù)是200,原來的“100”被丟棄。如果接著寫一句代碼:
i=i+1
則意味著先取出“整數(shù)瓶子”中裝的數(shù),把這個(gè)整數(shù)加一,之后再重新裝到瓶子中,最終瓶子內(nèi)保存的數(shù)是201。
下面從軟件技術(shù)的角度來解釋變量。
每個(gè)變量一定屬于一種數(shù)據(jù)類型,表2-5中列出了VB.NET常用的數(shù)字類型,并給出了每種數(shù)據(jù)類型所占的存儲(chǔ)空間。定義一個(gè)變量,其實(shí)就是在內(nèi)存中分配一個(gè)剛好可以容納這個(gè)數(shù)據(jù)類型的空間(比如Integer占4個(gè)字節(jié));向變量賦值,其實(shí)就相當(dāng)于把這個(gè)值寫到這個(gè)變量所代表的內(nèi)存空間中。請看以下代碼:
Dim i as Integer計(jì)算機(jī)執(zhí)行完上述語句之后,其內(nèi)存分配如圖2-45所示。對于VB.NET,如果定義一個(gè)整數(shù)變量時(shí)沒有指定其初始值,則自動(dòng)設(shè)置為“0”。
 技術(shù)內(nèi)幕
在VB.NET中,定義數(shù)字類型的變量(不管是整數(shù)還是小數(shù))都會(huì)自動(dòng)初始化為0值;定義一個(gè)字符串變量,則自動(dòng)初始化為空串;定義一個(gè)對象類型的變量,則自動(dòng)初始化為Nothing。
對于C/C++這樣的語言,定義一個(gè)變量只會(huì)根據(jù)這個(gè)變量的類型而在內(nèi)存中分配一個(gè)空間,但并不會(huì)自動(dòng)給定一個(gè)初值。因此,在用C/C++編程時(shí),在定義一個(gè)變量的同時(shí)給定一個(gè)初始值是一個(gè)良好的習(xí)慣。
執(zhí)行以下語句給變量i賦值:
i=100
上述代碼執(zhí)行之后,內(nèi)存中的情況如圖2-46所示。
變量之間可以相互賦值,請看以下代碼:
Dim i As Integer = 100
Dim j As Integer
j = i
上述代碼執(zhí)行完畢以后,其內(nèi)存布局如圖2-47所示。
從圖2-47中可以看到,變量i賦值給變量j之后,兩個(gè)變量的值都是100了,而且這兩個(gè)變量所代表是兩個(gè)獨(dú)立的內(nèi)存區(qū)域。與數(shù)學(xué)中的等號含義不同,此處的“=”在軟件中稱為賦值運(yùn)算符,語句“j = i”表示把變量i所代表的內(nèi)存區(qū)域中的值傳給j所代表的區(qū)域,但并不意味著在任何時(shí)候i一定等于j。
在編程過程中,心中有一個(gè)明晰的內(nèi)存布局圖是非常重要的,對于排除程序錯(cuò)誤大有幫助。在后面介紹類時(shí),還會(huì)對變量的內(nèi)存布局進(jìn)行更多的介紹。
3.強(qiáng)類型語言
所謂強(qiáng)類型的計(jì)算機(jī)語言,是指使用這種語言時(shí),所有的變量都必須先聲明后使用。早期的VB(6.0版本以前)可以不先定義變量而直接使用它,以下代碼在VB中是正確的:
i=100
i=i+1
除非顯式地將以下命令放到代碼文件開頭(注意不要放在Sub過程或函數(shù)體內(nèi)):
Option Explicit On
VB才會(huì)報(bào)告“變量未定義”錯(cuò)誤。
在新的VB.NET中,這個(gè)開關(guān)是默認(rèn)打開的,這就意味著在VB.NET中,所有變量都必須先聲明后使用。
 技術(shù)內(nèi)幕
除一些特殊情況外,不同類型的變量不能相互直接賦值。如果確實(shí)需要這樣做,就必須對變量進(jìn)行類型轉(zhuǎn)換。VB.NET可以自動(dòng)完成許多轉(zhuǎn)換工作,比如可以把一個(gè)整數(shù)直接賦值給字符串類型的變量。
VB.NET提供了一個(gè)新的命令用于設(shè)置類型轉(zhuǎn)換是否自動(dòng)進(jìn)行:
Option Strict { On | Off } '強(qiáng)制類型轉(zhuǎn)換開關(guān)
此開關(guān)默認(rèn)是關(guān)閉的,當(dāng)此開關(guān)打開時(shí),VB.NET將不會(huì)自動(dòng)進(jìn)行類型轉(zhuǎn)換。
打開此開關(guān)后,以下代碼將無法通過編譯:
Dim i As Integer=100
Dim s As String
s = i
必須修改為:
Dim i As Integer=100
Dim s As String
s =Ctr(i)
cstr()函數(shù)將整數(shù)轉(zhuǎn)為字符串。若將Option Strict一句刪除,原來的代碼就可以通過編譯了。
4.枚舉(Enum)
在程序中使用消息框是非常常見的,圖2-48是一個(gè)消息框的示例。
VB.NET中,可使用MsgBox()函數(shù)顯示消息框。
 技術(shù)探索
請使用MSDN查找MsgBox()函數(shù)的使用方法。
現(xiàn)在的問題是:怎樣知道用戶單擊的是哪個(gè)按鈕?
這是由MsgBox()函數(shù)的返回值來決定的。
MsgBox()函數(shù)返回如表2-6所示的值。
表2-6 MsgBox()函數(shù)返回值
返 回 值
含 義
MsgBoxResult.OK
1
按下【確定】按鈕
MsgBoxResult.Cancel
2
按下【取消】按鈕
MsgBoxResult.Abort
3
按下【中止】按鈕
MsgBoxResult.Retry
4
按下【重試】按鈕
MsgBoxResult.Ignore
5
按下【忽略】按鈕
MsgBoxResult.Yes
6
按下【是】按鈕
MsgBoxResult.No
7
按下【否】按鈕
可以看到,MsgBox()函數(shù)其實(shí)是返回一系列數(shù)字的,但這些數(shù)字都有一個(gè)名稱來表達(dá)(比如MsgBoxResult.OK)。這種類似于數(shù)字常量的數(shù)值稱為枚舉。MsgBoxResult是此枚舉類型的名字。枚舉類型的使用方法如下:
Dim ret As MsgBoxResult
ret = MsgBox("是否退出?", MsgBoxStyle.YesNo Or MsgBoxStyle.Information,_
"詢問操作")
If ret = MsgBoxResult.Yes Then
'單擊了Yes按鈕
Else
'單擊了No按鈕
End If
除了VB.NET預(yù)先定義的枚舉類型之外,還可以定義自己的枚舉類型,例如,可以為一組與一周中的7天相對應(yīng)的整型常數(shù)聲明一個(gè)枚舉,然后在代碼中使用這七天的名稱而不是它們的整數(shù)值。
Public Enum WeekDays
Sunday = 0
Monday = 1
Tuesday = 2
Wednesday = 3
Thursday = 4
Friday = 5
Saturday = 6
End Enum
現(xiàn)在,WeekDays.Sunday就表示0這個(gè)數(shù)字了。
一般用Enum語句在類的外部或模塊的聲明部分定義枚舉類型,這樣在整個(gè)類或項(xiàng)目中就都可以使用這個(gè)數(shù)據(jù)類型了。
 注意
在VB.NET中,幾乎所有的代碼(除了一些特殊類型的語句,如前面介紹的Option語句)都必須放在類(Class)或模塊(Module)中,沒有獨(dú)立存在的函數(shù)和Sub過程。
5.?dāng)?shù)組(Array)
數(shù)組用于存放一批具有相同類型的元素,這點(diǎn)與一般的變量是不一樣的。一個(gè)定義為Integer類型的變量只能存入一個(gè)整數(shù),而一個(gè)整數(shù)數(shù)組則可以存入一組整數(shù)。
以下代碼定義了一個(gè)數(shù)組:
Dim myIntegers() As Integer = {99, 32, 100, 16}
這個(gè)數(shù)組按如圖2-49所示的形式存儲(chǔ)數(shù)字。
數(shù)組中的元素按順序存放,每個(gè)元素都有一個(gè)位置編號(稱為數(shù)組元素下標(biāo)),從0開始。
訪問數(shù)組元素是通過下標(biāo)進(jìn)行的,格式為:
數(shù)組名(元素下標(biāo)值)
例如,以下代碼取出數(shù)組中的第3個(gè)元素值并用消息框顯示出來:
MsgBox(myIntegers(2))
依次訪問全部數(shù)組元素有兩種方法:
(1)可以使用循環(huán)語句來訪問全部的數(shù)組元素:
Dim myIntegers() As Integer = {99, 32, 100, 16}
Dim i As Integer
For i = 0 To myIntegers.GetLength(0) - 1
MsgBox(myIntegers(i))
Next
注意數(shù)組其實(shí)是一個(gè)對象,GetLength()是它的一個(gè)方法,用于獲取數(shù)組中的元素個(gè)數(shù)。
上述代碼把所有的數(shù)組元素遍歷了一遍,并用消息框顯示出來。
(2)可以使用For Each語句來訪問全部的數(shù)組元素:
Dim myIntegers() As Integer = {99, 32, 100, 16}
Dim i As Integer
For Each i In myIntegers
MsgBox(myIntegers(i))
Next
使用For Each語句除了可以訪問數(shù)組,還可以訪問集合(將在2.2.7節(jié)中介紹)。
6.結(jié)構(gòu)(Structure)
可以合并不同類型的變量來創(chuàng)建“結(jié)構(gòu)”。聲明結(jié)構(gòu)后,它成為一種復(fù)合的數(shù)據(jù)類型,進(jìn)而可以聲明該類型的變量。
除變量外,結(jié)構(gòu)還可以有屬性、方法和事件。
想讓單個(gè)變量包含有幾個(gè)相關(guān)信息時(shí)結(jié)構(gòu)很有用。例如,可能想將一個(gè)學(xué)生的姓名、年齡和學(xué)號信息放在一起??梢詫@些信息分別定義幾個(gè)變量,再將這些變量組合為一個(gè)結(jié)構(gòu)以簡化信息的處理。
(1)定義結(jié)構(gòu)
使用Structure語句作為結(jié)構(gòu)聲明的開始,并使用End Structure語句作為結(jié)構(gòu)聲明的結(jié)束。在這兩條語句之間必須至少聲明一個(gè)成員。成員可以屬于任何數(shù)據(jù)類型。
如果要將學(xué)生的姓名、年齡和學(xué)號信息一起保存在單個(gè)變量中,可以為這些信息聲明一個(gè)結(jié)構(gòu),如下所示:
Public Structure Student
Public SerialNo As String '學(xué)號
Public Name As String '姓名
Public Age As Integer '年齡
End Structure
(2)使用結(jié)構(gòu)變量
創(chuàng)建了結(jié)構(gòu)后,即可聲明該類型的變量:
Dim Stu1,Stu2 As Employee
通過以下方式來使用結(jié)構(gòu)變量:
變量名.結(jié)構(gòu)成員名
示例代碼如下:
Dim Stu1,Stu2 As Student
Stu1.SerialNo = "2004110107"
Stu1.Name = "張三"
Stu1.Age =20
Stu1和Stu2都是Employee類型的變量,因而可以相互賦值:
Stu2 = Stu1
其結(jié)果是Stu2中的所有成員值都與Stu1相同。
 注意
雖然賦值后Stu1和Stu2的所有成員值是一樣的,但這兩個(gè)變量完全是相互獨(dú)立的,可以給Stu1的Name屬性賦予一個(gè)新值(如“李四”),這并不會(huì)引起Stu2的Name屬性值改變,它仍是“張三”。
2.2.2 語句與控制結(jié)構(gòu)
在前面的學(xué)習(xí)中,讀者已經(jīng)看到并親自動(dòng)手編寫過一些程序代碼了。從這些代碼中可以發(fā)現(xiàn)一個(gè)個(gè)單詞構(gòu)成一個(gè)語句,多條語句被包在“Sub…End Sub”內(nèi)部(也可能是“Function…End Function”內(nèi)部),多個(gè)Sub和Function又被包在“Class…End Class”內(nèi)部。
在計(jì)算機(jī)編程語言(以VB.NET為例)中,把單詞稱為標(biāo)志符(Identifier),由標(biāo)志符構(gòu)成語句(Statement),由語句構(gòu)成函數(shù)(Function)或過程(Sub),由函數(shù)或過程構(gòu)成類(Class),由類構(gòu)成程序(Programme)。
在面向?qū)ο蟮某绦蛑?,類是最重要的組成元素,但要編寫一個(gè)類,還得從寫好一條條的語句開始。
1.語句的基本概念
一條計(jì)算機(jī)語句完成一個(gè)特定的功能,在VB.NET中,一行就是一條語句,語句由標(biāo)識符構(gòu)成,標(biāo)識符不區(qū)分大小寫。
如果某行語句太長了,可以加入換行符(即一個(gè)空格加一個(gè)下劃線),例如:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles Button1.Click
上述示例中,雖分為三行,卻只是一條語句。
 注意
不能在一個(gè)字符串內(nèi)部換行,以下代碼是不合法的:
Dim str As String = "我們一定能學(xué)好VB.NET,開發(fā)出好軟件, _
讓比爾 蓋茨也破產(chǎn)!"
 技術(shù)內(nèi)幕
在C#、Java等語言中,語句是以分號“;”作為結(jié)尾的,可以在標(biāo)識符和字符串之外的任意地方按下回車鍵把語句分成多行。
在一個(gè)語句前面加上單引號“'”,會(huì)讓此語句成為注釋語句。
注釋語句主要用于說明程序代碼的作用。
 注意
在計(jì)算機(jī)語言中使用的各種標(biāo)點(diǎn)符號都是英文半角的,在代碼中不要使用中文的標(biāo)點(diǎn)符號。
 提示
(1)在VS .NET代碼編輯器中選中多行代碼后,單擊“文本編輯器”工具欄上的 
 按鈕,會(huì)自動(dòng)把選中的代碼改為注釋語句,單擊 
 按鈕,則可以取消注釋。
如果您在VS .NET工具欄上沒看到這兩個(gè)按鈕,請?jiān)诠ぞ邫谏蠁螕羰髽?biāo)右鍵,從彈出菜單中選擇“文本編輯器”,調(diào)出此工具欄。
(2)在程序中加入適當(dāng)?shù)淖⑨屖欠浅V匾模鼓谶^了一段時(shí)間之后仍能很快地回憶起當(dāng)初寫這段代碼時(shí)的情況,從而為擴(kuò)充軟件功能、查找并修改錯(cuò)誤帶來很多方便。
一般而言,需要在類文件的開頭說明類的用途,在函數(shù)或過程的開頭說明其功能,一些重要的語句也需要加上注釋。
再次強(qiáng)調(diào),不要因?yàn)橥祽卸粚懽⑨?,在第一次寫代碼時(shí)花點(diǎn)時(shí)間寫注釋,不僅有利于理清思路,而且,這些時(shí)間將在后面的程序維護(hù)工作中成倍地節(jié)省回來!
2.控制結(jié)構(gòu)
現(xiàn)在的許多軟件系統(tǒng)規(guī)模都十分龐大,有的包含多達(dá)數(shù)百萬行代碼。但不管其結(jié)構(gòu)多么復(fù)雜,都是由三種最基本的控制結(jié)構(gòu)組成的。
這三種控制結(jié)構(gòu)就是順序、選擇與循環(huán)。
順序結(jié)構(gòu)如圖2-50所示。
圖2-50中,A和B都代表程序代碼塊,程序運(yùn)行時(shí),計(jì)算機(jī)先執(zhí)行完A代碼塊再執(zhí)行B代碼塊。
實(shí)際程序中的大部分代碼都是這樣順序執(zhí)行的。
再看看如圖2-51所示的選擇結(jié)構(gòu)。
圖2-51在執(zhí)行A和B代碼塊之前,先進(jìn)行一個(gè)條件判斷,如果條件滿足,則執(zhí)行A代碼塊,否則,執(zhí)行B代碼塊。
所有的計(jì)算機(jī)高級語言都有實(shí)現(xiàn)選擇結(jié)構(gòu)的“選擇語句”。
圖2-52所示為循環(huán)結(jié)構(gòu)。
 
 
順序結(jié)構(gòu)圖2-51 選擇結(jié)構(gòu)圖2-52 循環(huán)結(jié)構(gòu)
所謂循環(huán)結(jié)構(gòu),就是不斷地反復(fù)執(zhí)行一段代碼,由圖2-52可見,只要滿足一個(gè)特定的條件,代碼塊A將被不斷地執(zhí)行。
所有的計(jì)算機(jī)高級語言也都提供了自己的“循環(huán)語句”。
計(jì)算機(jī)科學(xué)家已經(jīng)證明:
再復(fù)雜的程序結(jié)構(gòu),最終都可以分解為順序、選擇與循環(huán)三種最基本的程序結(jié)構(gòu)。
不同的計(jì)算機(jī)語言實(shí)現(xiàn)這三種結(jié)構(gòu)的方式略有不同,其中有的計(jì)算機(jī)語言還做了適當(dāng)擴(kuò)充。下面介紹VB.NET中實(shí)現(xiàn)選擇結(jié)構(gòu)和循環(huán)結(jié)構(gòu)的語句。
(1)選擇語句
選擇語句用于實(shí)現(xiàn)選擇結(jié)構(gòu),其基本語法是:
If 條件 Then
'執(zhí)行語句塊A
Else
'執(zhí)行語句塊B
End If
舉個(gè)例子,把下面一句話變成計(jì)算機(jī)的語句結(jié)構(gòu):
如果天氣晴朗,我們就去爬香山,否則,我們待在家里看電視。
用程序設(shè)計(jì)語言表達(dá),其結(jié)果就是:
If 天氣晴朗 Then
我們?nèi)ヅ老闵?div style="height:15px;">
Else
我們待在家里看電視
End If
其中,“天氣晴朗”就是判斷條件。凡是用做條件判斷的語句一定有兩個(gè)值:真或者假。在VB.NET中,真用True表示,假用False表示。
所以,上面這個(gè)例子又可以寫成:
If 天氣晴朗=True Then
我們?nèi)ヅ老闵?div style="height:15px;">
Else
我們待在家里看電視
End If
“天氣晴朗=True”,這種等式稱為“邏輯表達(dá)式”。條件語句就是依據(jù)邏輯表達(dá)式的值是True還是False來決定執(zhí)行哪個(gè)語句塊的,“True”表示條件成立,“False”表示條件不成立。如果邏輯表達(dá)式中要表達(dá)的意思是True,則可以省略“=True”這一部分。比如上面的例子中,可以寫為“天氣晴朗=True”,也可以簡寫為“天氣晴朗”。
對于單個(gè)條件可以用上面的方式表達(dá),但如果需要有兩個(gè)條件進(jìn)行判斷,那又如何處理呢?比如張三對李四說:
如果天氣晴朗并且您有空,我們就去爬香山,否則,我們待在家里看電視。
這時(shí),計(jì)算機(jī)接受的語句為:
If 天氣晴朗 And 李四有空 Then
張三李四一塊去爬香山
Else
張三李四待在家里看電視
End If
And是一種邏輯運(yùn)算符。只有當(dāng)And連接的兩個(gè)邏輯表達(dá)式都為真時(shí),整個(gè)邏輯表達(dá)式的結(jié)果才為真。可以把And對應(yīng)為中文的“并且”這個(gè)詞,以便于理解它的含義。
還有另外兩個(gè)邏輯運(yùn)算符:Not和Or。
Not邏輯運(yùn)算符實(shí)例:
If Not(天氣下雨) Then
我們?nèi)ヅ老闵?div style="height:15px;">
Else
我們待在家里看電視
End If
Or邏輯運(yùn)算符實(shí)例:
If 我發(fā)工資了 or 有人請客 Then
我就去大飯店吃頓好的
Else
我回宿舍泡康師傅方便面吃
End If
看了上面的實(shí)例,讀者一定知道“Not”相當(dāng)于中文中的“不”,而“Or”則相當(dāng)于中文中的“或”。
現(xiàn)在看一個(gè)真正的計(jì)算機(jī)實(shí)例:
Dim testGrade As Integer
testGrade = CInt(InputBox("請輸入《.NET軟件開發(fā)技術(shù)》這門課的考核成績分?jǐn)?shù)", _
"輸入成績"))
If testGrade > 60 Then
MsgBox("不錯(cuò),您過關(guān)了!", , "老爸評語")
Else
MsgBox("這么好玩的課程您居然沒過?少玩點(diǎn)游戲,多編程!", , "教師忠告")
End If
 試一試
將上述代碼放到一個(gè)Button對象的Click事件中運(yùn)行,看看結(jié)果。
上述代碼中,InputBox()函數(shù)用于顯示一個(gè)輸入框供用戶輸入信息。由于InputBox()返回的是一個(gè)字符串,而變量testGrade是整型的,所以,需要使用VB.NET內(nèi)部函數(shù)CInt()將字符串轉(zhuǎn)為整數(shù)。
另外,注意一下MsgBox()中出現(xiàn)了連續(xù)兩個(gè)逗號,為什么?
在MSDN中查找MsgBox()函數(shù)的定義:
Public Function MsgBox( _
ByVal Prompt As Object, _
Optional ByVal Buttons As MsgBoxStyle = MsgBoxStyle.OKOnly, _
Optional ByVal Title As Object = Nothing _
) As MsgBoxResult
可以看到從第二個(gè)參數(shù)開始都是以一個(gè)單詞“Optional”開頭的,在前面有“Optional”修飾的函數(shù)參數(shù)稱為可選參數(shù),在使用MsgBox()函數(shù)時(shí),可以不設(shè)置其值,但其位置必須留出來,所以,才會(huì)出現(xiàn)了代碼中有連續(xù)兩個(gè)逗號的情況。
可選參數(shù)都有一個(gè)默認(rèn)值,從MsgBox() 函數(shù)的聲明中可以看到,Buttons參數(shù)的默認(rèn)值是“MsgBoxStyle.OKOnly”。
在選擇語句中,Else語句可有可無。此外,If語句可以嵌套,如下所示:
If 條件 Then
If 條件 Then
'執(zhí)行語句塊A
Else
'執(zhí)行語句塊B
End If
Else
If 條件 Then
'執(zhí)行語句塊C
Else
'執(zhí)行語句塊D
End If
End If
嵌套層數(shù)沒有限制,但一般而言,嵌套超過3層的條件語句非常難以閱讀。
使用選擇語句時(shí),特別要注意Else子句與哪個(gè)If配套。
If 條件 Then
If 條件 Then
'執(zhí)行語句塊A
Else
'執(zhí)行語句塊B
End If
End If
上面的Else與第二層的If配套。
有一個(gè)基本原則:
Else子句總與離它最近的If配套。
當(dāng)一個(gè)變量可能有多個(gè)固定的取值時(shí),使用If語句有不方便的地方,如要將1到7這七個(gè)數(shù)字與星期的中英文名稱對應(yīng)起來,這時(shí),可用Select Case語句。
假設(shè)WeekDayNums變量存入了1到7這七個(gè)值之一,則以下代碼可以獲取對應(yīng)數(shù)字的星期中英文名稱:
Dim WeekDays As Integer
Dim ChineseName, EnglishName As String
WeekDays = 5
Select Case WeekDays
Case 1
ChineseName = "星期一"
EnglishName = "Monday"
Case 2
ChineseName = "星期二"
EnglishName = "Tuesday"
Case 3
ChineseName = "星期三"
EnglishName = "Wednesday"
Case 4
ChineseName = "星期四"
EnglishName = "Thursday"
Case 5
ChineseName = "星期五"
EnglishName = "Friday"
Case 6
ChineseName = "星期六"
EnglishName = "Saturday"
Case 7
ChineseName = "星期日"
EnglishName = "Sunday"
Case Else
ChineseName = "無效星期數(shù)字"
EnglishName = "Invalid Week Number"
End Select
這段代碼一目了然,沒什么好說的,特別注意最后一個(gè)Case子句:
Case Else
這句表明,如果WeekDays變量的值不是1到7之間的數(shù)字,則由此分支中的語句進(jìn)行處理。
(2)循環(huán)語句
VB.NET中的循環(huán)語句有許多種,我們只介紹兩種:For循環(huán)與While循環(huán)。
For循環(huán)語句常用于指定次數(shù)的循環(huán),其格式為:
For 循環(huán)變量=初值 to 終值 step 增量值
'語句
Next 循環(huán)變量
其含義是:多次重復(fù)執(zhí)行語句,循環(huán)變量從初值開始,每次增加一個(gè)“增量值”,直到循環(huán)變量等于或大于終值時(shí)才停止。
實(shí)例:
求以下算術(shù)式的和。
1+2+3+...+100=?
分析:
顯然必須把加法語句循環(huán)一百次,示例代碼如下。
Dim i As Integer
For i = 1 To 100 Step 1
'完成相加的語句
Next
需要有一個(gè)變量用于存放加法的結(jié)果,因此,在定義變量i的語句后面,再定義一個(gè)新變量Result,并初始化為0:
Dim Result As Integer = 0
當(dāng)循環(huán)開始時(shí),i =1,讓
Result=Result+i
這樣,Result中就存入了第一個(gè)數(shù)字“1”,第二次循環(huán)時(shí),i=2:
Result=Result+i
又加入第二個(gè)數(shù)字“2”,現(xiàn)在Result中包含的結(jié)果是:1+2,即3,由此不斷循環(huán)100次,最終:
Result=1+2+3+...+100
完整的代碼如下:
Dim i As Integer
Dim Result As Integer = 0
For i = 1 To 100 Step 1
'完成相加的語句
Result = Result + i
Next
MsgBox("1+2+...+100=" & Result)
上述代碼中字符串與Result變量之間連接是采用“&”運(yùn)算符,此運(yùn)算符完成的功能是把兩個(gè)字符串連接在一起構(gòu)成一個(gè)新字符串。如果要連接的不是字串變量(本例中Result是整數(shù)),它會(huì)自動(dòng)把整數(shù)轉(zhuǎn)為字符串,再與“1+2+...+100=”連接起來。
如果用“+”取代上面的“&”運(yùn)算符,則VB.NET在運(yùn)行時(shí)會(huì)報(bào)錯(cuò)(如圖2-53所示)。
圖2-53 類型轉(zhuǎn)換錯(cuò)誤
這是因?yàn)?#8220;+”運(yùn)算符在發(fā)現(xiàn)它左右有數(shù)字時(shí),總是嘗試把另一個(gè)操作數(shù)也變成數(shù)字,而“1+2+3+…+100=”顯然沒法轉(zhuǎn)為數(shù)字,所以出錯(cuò)了。
解決方法是采用VB.NET的內(nèi)部函數(shù)CStr()將Result變量轉(zhuǎn)為字符串,如下所示:
MsgBox("1+2+...+100=" + CStr(Result))
 試一試
如果要求出100以內(nèi)的奇數(shù)之和,應(yīng)怎樣修改代碼?
另一種常見的循環(huán)語句是While循環(huán)語句,其格式為:
While 條件表達(dá)式
[語句]
End While
只要給定條件表達(dá)式值為True就執(zhí)行循環(huán)體內(nèi)的語句。
While循環(huán)與For循環(huán)是等價(jià)的,從1加到100的例子,可以使用While循環(huán)改寫如下:
Dim i As Integer = 1
Dim Result As Integer = 0
While i <= 100
Result = Result + i
i = i + 1
End While
MsgBox("1+2+...+100=" + CStr(Result))
While循環(huán)有一個(gè)特性,就是它可以很方便地執(zhí)行不定次數(shù)的循環(huán),以下實(shí)例不斷地要求用戶輸入一個(gè)整數(shù),計(jì)算機(jī)自動(dòng)地計(jì)算從1加到此整數(shù)的和,直到用戶輸入“-1”為止。
Dim InputNum As Integer = 0
Dim Result As Integer = 0
Dim i As Integer
While InputNum <> -1
InputNum = CInt(InputBox("請輸入一個(gè)大于0的整數(shù),要結(jié)束輸入請輸入-1"))
i = 1
Result = 0
While i <= InputNum
Result += i
i += 1
End While
MsgBox("從1加到" & InputNum & "的結(jié)果是:" & Result)
End While
上面代碼中有一個(gè)以前沒見過的語句:
Result += i
“+=”稱為復(fù)合運(yùn)算符,上面的語句相當(dāng)于:
Result=Result+i
 技術(shù)探索
除了“+=”之外,還有“*=”、“/=”這樣的復(fù)合運(yùn)算符,請自行查閱MSDN了解這些運(yùn)算符,并學(xué)會(huì)使用。
還有一種很有意思的循環(huán),稱為“死循環(huán)”。它的樣子是這樣的:
While True
[語句]
End While
除非在“[語句]”部分中有退出的代碼,否則它將永遠(yuǎn)運(yùn)行下去。
要退出一個(gè)循環(huán),可以使用Exit語句。
While循環(huán)的上一個(gè)例子可以用“死循環(huán)”的方式改寫如下:
Dim InputNum As Integer = 0
Dim Result As Integer = 0
Dim i As Integer
While True
InputNum = CInt(InputBox("請輸入一個(gè)大于0的整數(shù),要結(jié)束輸入請輸入-1"))
If InputNum = -1 Then
Exit While
End If
i = 1
Result = 0
While i <= InputNum
Result += i
i += 1
End While
MsgBox("從1加到" & InputNum & "的結(jié)果是:" & Result)
End While
注意,上面的示例代碼中使用一個(gè)條件語句檢測是否用戶輸入了“-1”,一旦確認(rèn),就調(diào)用Exit While語句退出循環(huán)。
 提示
任何一個(gè)“死循環(huán)”都一定要提供一個(gè)退出的出口,不然,您的程序就會(huì)失去響應(yīng)。
在上面這個(gè)例子中,循環(huán)的次數(shù)是未知的,何時(shí)結(jié)束完全由InputNum這個(gè)變量來決定,因此,可把這個(gè)變量稱為“哨兵”,這種類型的循環(huán)為“哨兵監(jiān)控”的循環(huán),而前面介紹的已知次數(shù)的循環(huán)可稱為“計(jì)數(shù)器”類型的循環(huán)。
 技術(shù)探索
VB.NET還提供了Do…Loop While和Do Until…Loop循環(huán)語句,請自行查閱MSDN文檔,了解相關(guān)的知識。
各種類型的循環(huán)語句都是等價(jià)的,可以用一種類型的循環(huán)語句替換另一種類型的循環(huán)語句而不會(huì)影響到程序的功能。
至此,介紹完了VB.NET中最基本的編程元素。下面將介紹如何把多個(gè)語句組合成函數(shù)和過程,并進(jìn)一步裝配出一個(gè)類。
2.2.3 對象與類
在上一節(jié)中學(xué)習(xí)了基本的VB.NET編程元素——語句,多個(gè)語句組合在一起,共同完成一個(gè)功能,稱為Sub過程或函數(shù)。
多個(gè)Sub過程或函數(shù)加上多個(gè)變量聲明,以及其他一些組成元素,就構(gòu)成了類。
在本小節(jié)中將介紹Sub過程、函數(shù)和類的基礎(chǔ)知識。
1.Sub過程
在VB.NET中,完成某個(gè)功能的多條語句可以放在一起,包含在Sub語句和End Sub語句中,這樣的一個(gè)代碼集合稱為“Sub過程”。每次調(diào)用Sub過程時(shí)都執(zhí)行Sub過程中的語句,從Sub語句后的第一個(gè)可執(zhí)行語句開始,到遇到的第一個(gè)End Sub、Exit Sub或Return語句結(jié)束。
聲明Sub過程的語法如下所示:
Sub 過程名[(參數(shù)列表)]
'VB.NET語句
End Sub
來看一個(gè)Sub過程實(shí)例,這個(gè)Sub過程接收一個(gè)圖像文件的完整路徑名,并把這個(gè)圖像顯示在窗體上。
新建一個(gè)Windows應(yīng)用程序項(xiàng)目(取名SubAndFunction,源代碼見配套光盤),切換到Form1的代碼視圖,在End Class語句之前的空白處輸入以下代碼:
'將指定的圖片畫到窗體上
Private Sub DrawPicToForm(ByVal fileName As String)
Dim img As Bitmap
'裝入圖片
img = New Bitmap(fileName)
'獲取窗體的繪圖對象
Dim g As Graphics
g = Me.CreateGraphics
'將圖片繪制到窗體上去,左上角坐標(biāo)為(0,0)
g.DrawImage(img, New Point(0, 0))
'釋放繪圖對象
g.Dispose()
End Sub
往窗體上拖入一個(gè)Button,設(shè)其名字為“btnDrawPic”,再從工具箱中選擇OpenFileDialog組件拖到窗體上,設(shè)置其Filter屬性為“所有圖像文件|*.jpg;*.gif;*.bmp;*.ico;*.wmf|所有文件|*.*”。
 提示
OpenFileDialog組件用于顯示標(biāo)準(zhǔn)的Windows打開文件對話框(參見圖2-54),而其Filter屬性用于指定顯示哪種類型的文件,其格式為:
提示信息一|*.文件擴(kuò)展名一|提示信息二|*.文件擴(kuò)展名二|……
如果想在一項(xiàng)中同時(shí)顯示多種擴(kuò)展名的文件,可以串聯(lián)多項(xiàng)“*.文件擴(kuò)展名”,中間以分號間隔(必須是英文的分號)。
給按鈕btnDrawPic的Click事件編碼如下:
Private Sub btnDrawPic_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles btnDrawPic.Click
Dim fileName As String
If Me.OpenFileDialog1.ShowDialog = DialogResult.OK Then
'如果用戶選擇了一個(gè)文件,并單擊了“打開”按鈕
fileName = Me.OpenFileDialog1.FileName
'顯示圖片
Me.DrawPicToForm(fileName)
End If
End Sub
程序運(yùn)行時(shí),單擊btnDrawPic按鈕,在對話框窗口中選取一個(gè)圖片,如圖2-54所示。
單擊【打開】按鈕之后,可以看到圖片出現(xiàn)在窗體上,如圖2-55所示。
 
圖2-54 使用OpenFileDialog組件選取圖片 圖2-55 調(diào)用Sub過程繪制圖片
在這個(gè)例子中,所有繪制圖片的功能都由Sub DrawPicToForm()過程完成。在按鈕的單擊事件處理代碼btnDrawPic_Click中,從OpenFileDialog控件的FileName屬性中取出用戶選擇文件的完整路徑名,并將其作為參數(shù)傳給Sub DrawPicToForm()過程。
Sub過程主要用于封裝完成某一特定功能的代碼。在本例中,需要向窗體上繪圖時(shí),只需要簡單地按“DrawPicToForm(圖像文件路徑名)”的格式調(diào)用就行了,不用重復(fù)地在不同地方書寫同樣的代碼。
細(xì)心的讀者可能會(huì)發(fā)現(xiàn),所有的控件事件處理代碼也都是Sub過程,只不過后面多了個(gè)Handles子句。Handles關(guān)鍵字將在第3章介紹。
2.函數(shù)
在現(xiàn)實(shí)中,如果某公司有一項(xiàng)工作任務(wù)需要完成,那么總經(jīng)理通常會(huì)把這個(gè)任務(wù)交給部門經(jīng)理去完成,并往往要求部門經(jīng)理在完成這個(gè)任務(wù)后提交一份總結(jié)報(bào)告。而部門經(jīng)理也往往不是親自動(dòng)手去做事,而是合理地把工作分配給不同的員工去完成,并在完成后向他匯報(bào)。這就是人類社會(huì)的協(xié)同工作方式。
在軟件中可以看到類似的組織方式。
函數(shù)就相當(dāng)于每個(gè)人需要完成的工作,在程序中體現(xiàn)為完成某種功能的代碼集合。公司員工在完成工作后需要向上級匯報(bào)結(jié)果,相應(yīng)地,函數(shù)在執(zhí)行完畢后也會(huì)有一個(gè)返回值。函數(shù)之間可以相互調(diào)用,這類似于部門經(jīng)理分派員工工作任務(wù)。
函數(shù)與Sub過程的區(qū)別就在于:
函數(shù)有返回值,Sub過程沒有。
 提示
在VB.NET中區(qū)分函數(shù)與Sub過程兩個(gè)不同的概念,而在C++、C#和Java中,沒有過程這個(gè)概念,Sub過程全部以返回void值的函數(shù)代替。
來看一個(gè)實(shí)例:編寫一個(gè)函數(shù)計(jì)算兩數(shù)之和。
Private Function AddTwoNumber(ByVal x As Single, _
ByVal y As Single) As Single
Dim result As Single
result = x + y
Return result
End Function
仔細(xì)看看上面的代碼,可以發(fā)現(xiàn)函數(shù)與Sub過程有以下不同點(diǎn):
(1)函數(shù)的關(guān)鍵字是Function(過程是Sub)。
(2)函數(shù)聲明最后有一個(gè)As子句,表明函數(shù)返回值的類型是單精度實(shí)數(shù)。
(3)代碼的最后有一個(gè)Return語句,把結(jié)果返回給了函數(shù)調(diào)用者。
使用此函數(shù)的實(shí)例如下:
MsgBox(AddTwoNumber(20, 30) + 100)
其結(jié)果是150。
可以看到,AddTwoNumber()函數(shù)可以用在表達(dá)式中,可以把它當(dāng)成一個(gè)數(shù)字來使用。
如果某個(gè)函數(shù)返回一個(gè)對象,比如String類型的對象,則函數(shù)本身就可以當(dāng)成一個(gè)字符串使用,下面是一個(gè)實(shí)例:
'將一個(gè)字符串反序
Private Function ReverseStr(ByVal str As String) As String
Return StrReverse(str)
End Function
使用實(shí)例:
MsgBox(ReverseStr("abcd").ToUpper)
代碼中“ReverseStr("abcd")”可以看成是一個(gè)String類型的對象,因而可以調(diào)用其ToUpper方法把字符串中的小寫字母全部變?yōu)榇髮?。這行代碼的最終結(jié)果為:“DCBA”。
現(xiàn)在可以給出函數(shù)的語法格式:
Private/Public Function 函數(shù)名(函數(shù)參數(shù)列表) As 返回值類型
'各種代碼
Return 返回值
End Function
3.類的定義
在面向?qū)ο蟪绦蛑校愂亲罨镜慕M成單位。換句話說,是由類構(gòu)成了整個(gè)程序,就如同磚塊構(gòu)建起了整棟大樓。
那么,類到底是什么?
可以把類看成是現(xiàn)實(shí)事物在計(jì)算機(jī)中的一種反映,舉個(gè)典型的例子——電視機(jī)。
一臺(tái)電視機(jī)具有以下的特性:
(1)生產(chǎn)廠家
(2)是否支持?jǐn)?shù)字信號(即是否可當(dāng)電腦顯示器用)
(3)價(jià)格
……
一臺(tái)電視機(jī)必須擁有以下的操作功能:
(1)打開、關(guān)閉
(2)選臺(tái)
(3)顯示當(dāng)前頻道數(shù)
……
如何在計(jì)算機(jī)中表達(dá)電視機(jī)這一概念?
可以使用變量來表示電視機(jī)所具有的特性,如表2-7所示。
表2-7 電視機(jī)特性
特 性
變 量 名
類 型
說 明
生產(chǎn)廠家
Producer
String
是否支持?jǐn)?shù)字信號
IsDigital
Boolean
為True表示數(shù)字電視
價(jià)格
Price
Single
用小數(shù)表示金額
當(dāng)前頻道
CurChannel
Integer
當(dāng)前的頻道
使用函數(shù)或Sub過程來表達(dá)電視機(jī)所擁有的功能,如表2-8所示。
表2-8 電視機(jī)功能
操作功能
函數(shù)或Sub過程名
參 數(shù)
打開
Sub Open()
關(guān)閉
Sub Close()
選臺(tái)
Sub ChooseChannel()
Channel(整型)
把上述的變量和函數(shù)(或Sub過程)用兩個(gè)語句“Class”和“End Class”包圍起來,再起一個(gè)名字,就形成了一個(gè)類,如下所示:
'電視機(jī)類
Public Class TV
'電視機(jī)的特性
Public Producer As String '生產(chǎn)廠家
Public IsDigital As Boolean '是否支持?jǐn)?shù)字信號
Public Price As Single '價(jià)格
Public curChannel As Integer '當(dāng)前頻道
'電視機(jī)的操作功能
'打開
Public Sub Open()
End Sub
'關(guān)閉
Public Sub Close()
End Sub
'選臺(tái)
Public Sub ChooseChannel(ByVal Channel As Integer)
End Sub
End Class
這個(gè)框架搭好以后,就可以往里面寫代碼了。
現(xiàn)在看看如何在電腦中“模擬出”人使用電視機(jī)的過程:
Dim myTV As TV
myTV = New TV '我買了一臺(tái)電視機(jī)
myTV.Producer = "海信" '是海信生產(chǎn)的電視機(jī)
myTV.Price = 2005 '2005元買的
myTV.IsDigital = True '是數(shù)字電視
'打開電視
myTV.Open()
'選擇第10頻道
myTV.ChooseChannel(10)
'我在看電視……
myTV.Close() '關(guān)閉電視機(jī)
可以看到在代碼中并不能直接使用一個(gè)類,必須先定義一個(gè)TV類的對象變量myTV,然后再用New關(guān)鍵字創(chuàng)建一個(gè)類的實(shí)例,之后才能使用這個(gè)類變量。
一定要區(qū)分類與類變量。類變量是一種變量,它的類型是某個(gè)具體的類。一個(gè)類可以創(chuàng)建許多個(gè)實(shí)例,類變量可以指向它們。一個(gè)比較容易理解的例子是可以把類看成是一個(gè)大印章,它可以在一張紙上蓋出無數(shù)個(gè)大印。這些蓋出的印就是印章類的實(shí)例(又可稱為印章對象)。
在上面的例子中,印章本身是前面寫的TV類及其全部源代碼,而myTV就是一個(gè)對象變量,它可以代表由印章(TV類)蓋出的一個(gè)?。═V類的一個(gè)實(shí)例),印章蓋印的過程是用New來表達(dá)的。
這些概念可能比較抽象難懂,看一個(gè)實(shí)例可能更易于理解。
打開VS .NET,創(chuàng)建一個(gè)“Windows應(yīng)用程序”項(xiàng)目,起名為UseClass(參見本書配套光盤)。
從“項(xiàng)目”菜單中選擇“添加類”,給類起名字“TV”,如圖2-56所示。
單擊【打開】按鈕之后,VS .NET將會(huì)在項(xiàng)目中創(chuàng)建好一個(gè)空的類框架代碼,在此代碼框架中敲入上面介紹的代碼,如圖2-57所示。
使用VS .NET創(chuàng)建類
接著,按如圖2-58所示設(shè)計(jì)窗體。
 
給類書寫代碼圖2-58 “電視機(jī)”示例主窗體
注意,在打開電視機(jī)之前,是不能選臺(tái)的,所以,程序運(yùn)行時(shí),【選臺(tái)】和【關(guān)閉】按鈕是灰色的。
切換到代碼視圖,在所有Sub過程的外部書寫以下代碼:
Private myTV As TV
這就在窗體類中定義了一個(gè)成員變量,此變量可以被窗體中的所有函數(shù)和Sub過程所訪問。
給【打開】按鈕的事件編碼:
Private Sub btnOpen_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnOpen.Click
myTV = New TV '我買了一臺(tái)電視機(jī)
myTV.Producer = "海信" '是海信生產(chǎn)的電視機(jī)
myTV.Price = 2005 '2005元買的
myTV.IsDigital = True '是數(shù)字電視
myTV.curChannel = 10 '設(shè)置10頻道
myTV.Open() '打開電視機(jī)電源
'顯示當(dāng)前頻道數(shù)
Me.lblChannel.Text = CStr(myTV.curChannel)
'讓按鈕可用
Me.btnClose.Enabled = True
Me.btnChooseChannel.Enabled = True
'不允許第二次打開電視機(jī)電源
Me.btnOpen.Enabled = False '讓自己變灰
End Sub
當(dāng)程序運(yùn)行后,單擊【打開】按鈕,運(yùn)行結(jié)果如圖2-59所示。
給【選臺(tái)】按鈕添加代碼如下:
myTV.curChannel = CInt(Me.txtChannel.Text)
'顯示當(dāng)前頻道數(shù)
Me.lblChannel.Text = CStr(myTV.curChannel)
編譯運(yùn)行,在文本框中輸入一個(gè)數(shù)字,單擊【選臺(tái)】按鈕,可以看到當(dāng)前頻道顯示出相應(yīng)的數(shù)字,如圖2-60所示,這是通過直接設(shè)置類的curChannel變量值來實(shí)現(xiàn)的。
 
圖2-59 正在看電視…… 圖2-60 換了一個(gè)臺(tái)
從上面例子可以看到,通過使用一個(gè)TV類對象在計(jì)算機(jī)中虛擬出了一臺(tái)電視機(jī),可以“打開”、“關(guān)閉”和“選臺(tái)”,所有這些功能都是通過函數(shù)和 Sub 過程來表達(dá)的。而當(dāng)前頻道屬于一種信息,在TV類對象中以變量來表達(dá)。
這個(gè)例子展示了許多重要的東西:
(1)它直觀地展示了現(xiàn)實(shí)中的事物是如何被抽象成計(jì)算機(jī)可以處理的一個(gè)類的。
(2)現(xiàn)實(shí)事物中的功能往往用類的函數(shù)和Sub過程來表示,而現(xiàn)實(shí)事物的屬性可以使用變量來表示。
在一個(gè)類中聲明的函數(shù)和Sub過程,在面向?qū)ο罄碚撝型Q為“方法(Method)”,而聲明的變量,則稱為類的“數(shù)據(jù)成員(Data Member)”或“字段(Field)”,有時(shí)也稱為“成員變量(Member Variable)”。另外,在C#、Java、C++這些面向?qū)ο蟮恼Z言中,不區(qū)分函數(shù)和Sub過程,全部以函數(shù)稱之。
4.給類設(shè)置屬性
前面TV類提供了一個(gè)curChannel字段,這是一個(gè)類型為Integer的整數(shù),但實(shí)際上,電視臺(tái)的頻道數(shù)是有限的,而且不可能有小于0的頻道數(shù)。如何在TV類中表現(xiàn)這種附加的信息呢?這就引入了另一個(gè)非常重要的概念——對象的屬性。
在TV類中,如何表達(dá)電視臺(tái)數(shù)是有限的這一條件?
解決方法是定義一個(gè)常量:
Const MAX_CHANNEL As Integer = 100
現(xiàn)在即可定義一個(gè)curChannel屬性,先把以下這條語句刪除:
Public curChannel As Integer '當(dāng)前頻道
加入以下代碼:
Private _curChannel As Integer = 0
Public Property curChannel() As Integer '當(dāng)前頻道
Get
Return _curChannel
End Get
Set(ByVal Value As Integer)
'電視頻道數(shù)必須大于0,小于最大的頻道數(shù)
If Value >= 0 And Value <= MAX_CHANNEL Then
_curChannel = Value
Else
_curChannel = 0
End If
End Set
End Property
上述代碼中有一些有趣的東西。
Property是一個(gè)關(guān)鍵字,表明curChannel現(xiàn)在不是一個(gè)簡單的變量,而是一個(gè)屬性了。一個(gè)屬性由兩個(gè)特殊的過程來實(shí)現(xiàn)。
(1)Get過程:當(dāng)讀此屬性時(shí),會(huì)執(zhí)行此過程。
(2)Set過程:當(dāng)向此屬性賦值時(shí),會(huì)執(zhí)行此過程。注意到這一過程有一個(gè)Value參數(shù),在使用屬性的代碼中對此屬性所賦的值被Value參數(shù)所捕獲。
一個(gè)屬性并不能真正保存東西,真正能保存信息的是變量,所以,絕大多數(shù)屬性都使用一個(gè)類的私有數(shù)據(jù)成員變量來保存此信息。比如上例中_curChannel變量就是一個(gè)用于保存信息的變量:
Private _curChannel As Integer = 0
_curChannel的默認(rèn)值在定義時(shí)直接用賦值運(yùn)算符指定為0。
特別要注意這個(gè)變量被聲明為私有的(Private)。
 技術(shù)內(nèi)幕
如果一個(gè)類的數(shù)據(jù)成員被聲明為私有的,則類外部的任何代碼都不能訪問此數(shù)據(jù)成員。
例如,以下類聲明了一個(gè)公有和一個(gè)私有的數(shù)據(jù)成員:
Class ShowPrivateAndPublic
Private i As Integer
Public j As Integer
End Class
可以使用這個(gè)類創(chuàng)建一個(gè)對象,并訪問這個(gè)變量的j字段,如下所示:
Dim obj As ShowPrivateAndPublic
obj = New ShowPrivateAndPublic
obj.j = 100
但是不能寫出以下代碼:
Obj.i=100
因?yàn)閕是私有的,所以不能在類的外部訪問變量i。但在類ShowPrivateAndPublic中如果有Sub過程和函數(shù),則其中的代碼可以訪問變量i。
再回來看看例子中實(shí)現(xiàn)屬性的代碼,當(dāng)需要獲取屬性的值時(shí),僅須簡單地返回_curChannel變量值就行了,而當(dāng)設(shè)置屬性值時(shí),先判斷一下值是否有效,有效就設(shè)置_curChannel變量為期望的值,否則,一律置為0。
可以在屬性的Get和Set過程中設(shè)置程序斷點(diǎn),從而看到屬性的代碼執(zhí)行流程。
 提示
在調(diào)試(DEBUG)狀態(tài)下,給某句代碼設(shè)置斷點(diǎn)后,VS .NET在執(zhí)行到此句時(shí)會(huì)停在此句,可以在此時(shí)檢查程序的運(yùn)行狀況(比如變量值)。程序斷點(diǎn)是排除程序錯(cuò)誤的有效工具。
用鼠標(biāo)在某行代碼的最左端單擊,可以定義一個(gè)斷點(diǎn)。
參見圖2-61,在代碼中定義的兩個(gè)斷點(diǎn)以褐色的小圓點(diǎn)表示,同時(shí)相應(yīng)的行被高亮選中:
設(shè)置斷點(diǎn)
按F5鍵運(yùn)行程序,單擊【打開】按鈕,會(huì)看到如圖2-62所示的情形。
圖2-62 程序中斷
由于【打開】按鈕的事件處理代碼中有一句:
myTV.curChannel = 10  '設(shè)置10頻道
所以,應(yīng)該會(huì)執(zhí)行TV類curChannel屬性的Set過程。由于在Set過程中事先設(shè)置了一個(gè)斷點(diǎn),所以,程序在Set過程中指定的代碼處中斷,VS .NET自動(dòng)顯示對應(yīng)的代碼,并用一個(gè)小小的黃色圖標(biāo)指明將要執(zhí)行的下一條語句。
把鼠標(biāo)移到Value這一過程參數(shù)上,可以看到VS .NET會(huì)彈出一個(gè)小提示窗口,顯示Value=10,這就說明在【打開】按鈕的事件處理代碼中給屬性賦的值10被屬性Set過程的Value參數(shù)接收了。
 提示
當(dāng)程序處于中斷提示狀態(tài)時(shí),可以在VS .NET的命令窗口中執(zhí)行任何有效的VB.NET命令。最常用的是顯示特定對象某個(gè)變量或?qū)傩缘闹担梢杂靡粋€(gè)問號來顯示當(dāng)前變量的值,如圖2-63所示。
圖2-63 使用命令窗口跟蹤變量值
命令窗口的功能是非常強(qiáng)大的,它讓我們可以隨時(shí)執(zhí)行一個(gè)有效的VB.NET命令,靈活應(yīng)用命令窗口可以為調(diào)試程序帶來很大的方便。
現(xiàn)在請按F5鍵,讀者會(huì)發(fā)現(xiàn)程序暫停到了Get過程,這是由另一句代碼引發(fā)的:
'顯示當(dāng)前頻道數(shù)
Me.lblChannel.Text = CStr(myTV.curChannel)
這就說明了讀取屬性值時(shí),會(huì)自動(dòng)調(diào)用Get過程。
 提示
使用屬性最容易出錯(cuò)的地方就是沒有定義一個(gè)對應(yīng)的私有變量,并給Get 和 Set過程書寫代碼。在這種情況下,程序會(huì)照常運(yùn)行,不會(huì)引發(fā)錯(cuò)誤,但屬性值在任何情況下都不會(huì)變(保持VB.NET的默認(rèn)值,比如Integer類型的屬性值為0 ,String類型的屬性值為空串)。
現(xiàn)在有一個(gè)問題,屬性和類的公有成員變量都可以用來存儲(chǔ)信息,那么什么時(shí)候用公有變量,什么時(shí)候用屬性?
以一個(gè)表示公司員工的類為例說明:
一般而言,當(dāng)需要對類的某個(gè)信息做出某種限定(比如規(guī)定姓名不能為空,工資金額不可能是負(fù)數(shù)),或者是某個(gè)信息依賴于類的其他信息計(jì)算得出(比如年齡,它可以根據(jù)人的出生年月與當(dāng)前年現(xiàn)場計(jì)算出來)時(shí),使用屬性比較合適。
一些信息如果沒有什么限制,就可以使用公有變量。
 試一試
請仔細(xì)分析以下現(xiàn)實(shí)事物的特性,設(shè)計(jì)類來表達(dá)這些特性。注意正確決定哪些特性用屬性,哪些特性用類的公有成員變量。
汽車、書、U盤
 技術(shù)探索
在本節(jié)中介紹的是既可以讀也可以寫的屬性。事實(shí)上,VB.NET還提供只讀與只寫屬性,分別以ReadOnly和WriteOnly兩個(gè)關(guān)鍵字表達(dá)。請?jiān)贛SDN中通過查找這兩個(gè)關(guān)鍵字掌握只讀與只寫屬性的使用方法。
5.名字空間與類
在前面讀者已經(jīng)接觸過名字空間的概念了,本節(jié)在原有基礎(chǔ)上加深讀者對這一概念的認(rèn)識。
.NET Framework使用名字空間(Name Space)來管理所有的類。如果把類比喻成書的話,則名字空間就是用于放書的地方,書保存放在書架上,類放在名字空間里。
當(dāng)我們?nèi)D書館查找一本書時(shí),需要指定這本書的編號,編號往往規(guī)定了書放在哪個(gè)書庫的哪個(gè)書架上。通過逐漸縮小的范圍:圖書館→書庫→書架,最終可以在某個(gè)書架中找到這本書。
可以采用類似的方法來管理類,通過逐漸縮小的范圍:最大的名字空間→子名字空間→孫名字空間→……,最終找到一個(gè)類。
在代碼中可使用Namespace關(guān)鍵字定義自己的名字空間:
'父名字空間
Namespace NS1
Public Class Class1
End Class
'子名字空間
Namespace NS2
Public Class Class2
End Class
End Namespace
End Namespace
上述代碼定義了一個(gè)父名字空間NS1,其中放置了一個(gè)類Class1,又定義了一個(gè)子名字空間NS2,其中放置了另一個(gè)類Class2。
在同一項(xiàng)目中,可以這樣來使用這兩個(gè)類:
Dim o1 As New NS1.Class1
Dim o2 As New NS1.NS2.Class2
如果上述代碼放在一個(gè)類庫項(xiàng)目(項(xiàng)目名稱為ExampleClassLibrary)中,VS .NET默認(rèn)將項(xiàng)目名作為頂級的名字空間。所以,如果要在其他項(xiàng)目中使用Class1和Class2,就必須這樣來使用兩個(gè)類:
Dim o1 As New ExampleClassLibrary.NS1.Class1
Dim o2 As New ExampleClassLibrary.NS1.NS2.Class2
也可以使用Imports語句來簡化代碼的書寫:
Imports ExampleClassLibrary.NS1
Imports ExampleClassLibrary.NS1.NS2
'……
Dim o1 As New Class1
Dim o2 As New Class2
 注意
類庫項(xiàng)目編譯之后將生成一個(gè)DLL文件,另一個(gè)項(xiàng)目(比如Windows應(yīng)用程序)可以使用這個(gè)DLL文件中聲明為Public的類。使用方法是從“項(xiàng)目”菜單中選擇“添加引用”命令,如圖2-64所示。
在圖2-64中單擊【瀏覽】按鈕選中DLL文件,單擊【確定】按鈕,即將類庫引用加入到了項(xiàng)目中,現(xiàn)在即可在項(xiàng)目中使用Imports語句來引入類庫中定義的名字空間(參見上面的代碼)。
添加項(xiàng)目引用
事實(shí)上,所有的.NET Framework中的類都必須通過名字空間來引用。在“解決方案資源管理器”的項(xiàng)目節(jié)點(diǎn)中就有一個(gè)“引用”子節(jié)點(diǎn),其中列出了項(xiàng)目中所有引用的類庫,參見圖2-65。
在圖2-65中雙擊類庫節(jié)點(diǎn)ExampleClassLibrary,即可打開“對象瀏覽器”查看類庫中的所有名字空間。
 
項(xiàng)目引用的類庫節(jié)點(diǎn) 圖2-66 通過對象瀏覽器查看名字空間
6.使用圖形來表示類
在UseClass這個(gè)實(shí)例中,讀者是通過閱讀源代碼來了解類的定義的。但在一個(gè)真實(shí)的軟件中,可能有幾十個(gè)甚至上百個(gè)類,每個(gè)類可能提供數(shù)十種方法,這些類之間又存在著復(fù)雜的關(guān)系(比如將在第3章中介紹的繼承和多態(tài)),要閱讀這么多的代碼才能了解類的功能,顯然是非常麻煩且低效的。
想想電子工程師是怎么設(shè)計(jì)電路板的?他們使用抽象的符號來表達(dá)復(fù)雜的電路回路。我們在高中都學(xué)過電阻、電容等標(biāo)準(zhǔn)的圖形符號,并都會(huì)畫物理電路圖來描述電路,參見圖2-67。
軟件工程師也有自己的圖,國際上的OMG(Object Management Group,對象管理組織)制定了一整套用于描述面向?qū)ο筌浖到y(tǒng)的標(biāo)準(zhǔn)圖形及相關(guān)標(biāo)準(zhǔn),即統(tǒng)一建模語言(Unified Modeling Language,UML),在后面的章節(jié)中,將經(jīng)常使用這種語言來描述程序。因此,先提前在本節(jié)介紹一些UML的基礎(chǔ)知識,本書第11章有更為詳盡的介紹。
在UML中,類是用類圖來表示的,前面設(shè)計(jì)的TV類用UML表示,如圖2-68所示。
把這個(gè)圖與TV類的源代碼相互對照,可以很容易明白這個(gè)圖的含義。
展示一個(gè)類的所有數(shù)據(jù)成員和方法的圖在UML中稱為類圖。一個(gè)類圖是一個(gè)有三欄的方框,第一個(gè)方框?qū)懮项惖拿郑诙€(gè)方框列出類中所有字段,第三個(gè)方框列出所有的方法。
圖2-68中的“+”表示Public,“-”表示Private,還有一個(gè)“#”號圖中沒有,它表示Protected。有關(guān)Protected的含義將在第3章介紹。
2.2.4 變量的類型
以下代碼定義了一個(gè)結(jié)構(gòu)和一個(gè)類用于表示一個(gè)點(diǎn)的坐標(biāo)(x,y):
Public Structure ValPoint
Dim x As Integer
Dim y As Integer
End Structure
Public Class RefPoint
Public x As Integer
Public y As Integer
End Class
一旦定義好了這兩個(gè)數(shù)據(jù)類型,就可以使用它們來定義變量。使用結(jié)構(gòu)ValPoint的示例代碼如下:
'測試值變量
Dim p1 As ValPoint
p1.x = 100
p1.y = 100
MsgBox(CStr(p1.x))
運(yùn)行上述代碼(可以參見本章示例代碼RefTypeAndValueType),可以看到程序正確地顯示p1.x的值為100。
現(xiàn)在使用類變量,示例代碼如下:
'測試引用變量
Dim p1 As RefPoint
p1.x = 100
p1.y = 100
MsgBox(CStr(p1.x))
可以看到除了第一句以外,所有代碼都是與使用結(jié)構(gòu)變量ValPoint一樣的。
運(yùn)行,看看出現(xiàn)了什么情況?
系統(tǒng)報(bào)告出現(xiàn)了一個(gè)錯(cuò)誤:“未將對象引用設(shè)置到對象的實(shí)例”(圖2-69)。
圖2-69 NullReferenceException異常
為什么會(huì)出現(xiàn)這種情況?
原來,變量是分類型的,定義為ValPoint類型的變量稱為值類型(Value Type)的變量,定義為RefPoint類型的變量稱為引用類型(Reference Type)的變量。如果一個(gè)變量是值類型的,那么一旦定義完后就可以直接使用了,而引用類型的變量則必須先創(chuàng)建(New)一個(gè)對象,之后才能使用,否則就會(huì)出現(xiàn)以上的錯(cuò)誤。
1.引用類型與值類型變量
引用類型與值類型到底差別何在?這涉及計(jì)算機(jī)內(nèi)存(Memory)的分配方式。
先來看值變量:
Dim p1 As ValPoint
此代碼執(zhí)行之后,內(nèi)存布局如圖2-70所示。
從圖2-70可以看到,如果定義了一個(gè)ValPoint類型的變量,那么計(jì)算機(jī)會(huì)在內(nèi)存中分配一塊空白區(qū)域,大小能容納值類型所定義的所有成員(ValPoint中有兩個(gè)類型為Integer的整型變量成員)。而變量名p1就代表了這塊內(nèi)存區(qū)域,可以通過p1.x直接訪問內(nèi)存中x的值。
但引用類型的變量就不是這樣了,當(dāng)計(jì)算機(jī)執(zhí)行完以下定義變量語句時(shí):
Dim p1 As RefPoint
內(nèi)存布局如圖2-71所示。
虛線框表示此內(nèi)存并未真正分配。從圖2-71中可以看到,定義一個(gè)引用變量并不會(huì)導(dǎo)致計(jì)算機(jī)為此變量分配足夠容納此變量所包含所有成員數(shù)據(jù)的內(nèi)存空間,而只是分配了一個(gè)固定大小的空間(4個(gè)字節(jié))并將其內(nèi)容設(shè)為0。這個(gè)被初始化為0的空間即p1本身,其內(nèi)存代表真實(shí)的數(shù)據(jù)在內(nèi)存中的位置(又稱為內(nèi)存地址)。
正因?yàn)榭扇菁{x和y的內(nèi)存還未分配,p1.x是不存在的,所以,MsgBox(CStr(p1.x))會(huì)引發(fā)NullReferenceException異常。
修正錯(cuò)誤很簡單,只需要增加一句就行了,代碼如下所示:
'測試引用變量
Dim p1 As RefPoint
p1 = New RefPoint
p1.x = 100
p1.y = 100
MsgBox(CStr(p1.x))
第2句代碼使用New關(guān)鍵字創(chuàng)建一個(gè)RefPoint對象,其實(shí)質(zhì)是讓計(jì)算機(jī)給變量分配可容納真實(shí)數(shù)據(jù)的內(nèi)存空間,執(zhí)行之后,內(nèi)存布局如圖2-72所示。
現(xiàn)在,p1.x就可以正確地訪問了。
 提示
在.NET編程中,一定要注意.NET Framework所提供的數(shù)據(jù)類型是引用類型還是值類型。有一個(gè)簡單的判斷方法:凡是類類型的變量一定是引用類型的。使用它之前一定要先創(chuàng)建一個(gè)對象出來。其他的類型都是值類型的,可以直接使用。
引用變量的內(nèi)存布局是十分重要的,如果理解不清,在今后的編程工作中會(huì)引發(fā)許多隱含的錯(cuò)誤。
請看以下問題:
Dim p1, p2 As RefPoint
p1 = New RefPoint
p1.x = 100
p1.y = 100
p2 = p1
問:
p2.x會(huì)不會(huì)引發(fā)錯(cuò)誤?如果沒錯(cuò),它的值是多少?
答案:
p2.x不會(huì)引發(fā)錯(cuò)誤,它的值等于100。
變量賦值之后,其內(nèi)存布局如圖2-73所示。
可以看到,p2=p1,其實(shí)就是把p1的內(nèi)容原樣復(fù)制一份給p2,所以,現(xiàn)在p1和p2都指向同一個(gè)對象。p2.x與p1.x都是同一個(gè)內(nèi)存區(qū)域。因而,可以通過任意一個(gè)變量(p1或p2)來修改x的值,另一個(gè)變量會(huì)同時(shí)感知到此值的變化。
 試一試
理解了本章所介紹的內(nèi)存變量分配模型,請做以下練習(xí):
1 Dim p1, p2 As RefPoint
2 p1 = New RefPoint
3 p1.x = 100
4 p1.y = 100
5 p2 = p1
6 p2 = New RefPoint
7 MsgBox(CStr(p2.x))
問:
(1)p2.x現(xiàn)在的值是多少?
(2)如果把第6句移到第3句前面,則p2.x的值又是多少?
(3)請繪出這兩種情況下的內(nèi)存分配模型。
上機(jī)編程驗(yàn)證您的推測。
現(xiàn)在再來看一下數(shù)組。
Dim ps(100) As ValPoint
Dim i As Integer
For i = 0 To 99
ps(i).x = i
ps(i).y = i
Next
上述代碼定義了一個(gè)有100個(gè)ValPoint元素的數(shù)組,并且通過循環(huán)初始化了數(shù)組元素。
再看以下代碼:
Dim ps(100) As RefPoint
Dim i As Integer
For i = 0 To 99
ps(i).x = i
ps(i).y = i
Next
能實(shí)現(xiàn)同樣的目的嗎?
請編程驗(yàn)證您的推測。
2.類內(nèi)部信息的相互傳送
先看如圖2-74所示的示例(參見示例VariantScope)。
當(dāng)單擊按鈕時(shí),窗體上的標(biāo)簽會(huì)顯示出單擊的次數(shù)。
按鈕對象名為“btnClickMe”,顯示次數(shù)是一個(gè)標(biāo)簽對象lblClickCount,這個(gè)功能如何實(shí)現(xiàn)?
經(jīng)過思索,讀者可能會(huì)寫出以下代碼:
1 Private Sub btnClickMe_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnClickMe.Click
2 Dim ClickCount As Integer
3 '單擊次數(shù)加1
4 ClickCount += 1
5 '用標(biāo)簽(Label)控件顯示單擊次數(shù)
6 Me.lblClickCount.Text = ClickCount
7 End Sub
然而很遺憾,不管單擊按鈕多少次,標(biāo)簽總顯示為“1”。
 試一試
(1)現(xiàn)在把第2句代碼移到Sub外面,再運(yùn)行試一試。
(2)注釋掉移動(dòng)到Sub外部的代碼。把原來的第2句代碼改為:
Static ClickCount As Integer
再試一試。
試驗(yàn)結(jié)果表明程序運(yùn)行結(jié)果正常。
上述兩個(gè)試驗(yàn)蘊(yùn)涵著重要的程序設(shè)計(jì)基礎(chǔ)知識。
最初定義的變量是在Sub過程內(nèi)部,稱為“局部動(dòng)態(tài)變量”,其特點(diǎn)是它的值在離開Sub過程后會(huì)被丟棄,下次執(zhí)行時(shí)又會(huì)重新設(shè)置(取其默認(rèn)值0),所以標(biāo)簽總顯示為“1”。
第一次試驗(yàn)中把這個(gè)變量定義在Sub過程之外,則此變量現(xiàn)在成了類的一個(gè)“成員變量”。在類內(nèi)部的Sub過程可以訪問并修改這個(gè)變量,變量的值能夠在Sub過程運(yùn)行結(jié)束之后繼續(xù)保存,再次運(yùn)行Sub過程時(shí),ClickCount變量的值就是上次運(yùn)行的結(jié)果。
第二次試驗(yàn)中變量還是定義在Sub過程之內(nèi),所不同之處在于使用“Static”關(guān)鍵字定義,在本例中,其效果與類的“成員變量”是一樣的。這種變量稱為“局部靜態(tài)變量”。
讀者可能會(huì)奇怪:類成員變量與局部靜態(tài)變量都可以記錄下上次Sub過程執(zhí)行的結(jié)果,那么它們在使用上有何差別?
實(shí)情是這樣的:
類的成員變量可以被類的任何方法(包括函數(shù)和Sub過程)所訪問,而局部靜態(tài)變量只能被本身(即它所定義于其中的函數(shù)或Sub過程)所訪問。
在實(shí)際開發(fā)中,經(jīng)常需要解決信息的流動(dòng)問題,這種流動(dòng)可以是在類與類之間的,也可以是類內(nèi)部的。類與類之間一般通過聲明為Public的屬性和方法相互交換信息,而類內(nèi)部的信息交換則主要通過類成員變量實(shí)現(xiàn)。如果對這些問題理解不清,在實(shí)際開發(fā)時(shí),可能會(huì)引發(fā)許多難以排除的錯(cuò)誤,因此,讀者一定要清晰地理解這些概念。
3.類的共享數(shù)據(jù)成員
類的成員(包括變量、函數(shù)和Sub過程)也可以是靜態(tài)的。其方法就是在定義成員時(shí)加上Shared關(guān)鍵字(注意不是Static)。
Public Class AllStaticMemberClass
Public Shared Counter As Integer
Public Shared Sub StaticSub()
'代碼略
End Sub
Public Shared Function StaticFunction() As Integer
'代碼略
End Function
End Class
要訪問類的靜態(tài)成員,有兩種方法。
(1)使用“類名.成員名”的方法,如:
AllStaticMemberClass.Counter = 0 '訪問共享變量
MsgBox(AllStaticMemberClass.StaticFunction()) '訪問共享函數(shù)
AllStaticMemberClass.StaticSub() '訪問共享Sub過程
(2)使用“對象.成員名”的方法:
Dim obj As New AllStaticMemberClass
obj.Counter = 0 '訪問共享變量
MsgBox(obj.StaticFunction) '訪問共享函數(shù)
obj.StaticSub() '訪問共享Sub過程
需要注意的是:凡聲明為Shared的類成員,不管這個(gè)類生成了多少個(gè)對象,它們都共享同一個(gè)類成員。
 提示
當(dāng)有些功能需要被許多個(gè)類所使用時(shí),將這些功能封裝成Shared的函數(shù)和Sub過程是比較好的選擇,可以避免創(chuàng)建對象所帶來的性能損失。
2.2.5 函數(shù)與方法重載
在前面所介紹的程序中消息框用得很多,讀者可能已經(jīng)習(xí)慣了使用MsgBox來向用戶提示一些信息。
如果使用C#,您會(huì)發(fā)現(xiàn)C#中沒有MsgBox函數(shù),而代之以MessageBox函數(shù)。事實(shí)上,MsgBox是VB.NET對于MessageBox函數(shù)的封裝。換句話說,MsgBox內(nèi)部調(diào)用MessageBox來顯示消息框。
 技術(shù)內(nèi)幕
MsgBox的歷史非常悠久,早在VB 3.0(20世紀(jì)90年代中期推出)時(shí)就提供這一函數(shù)了。除了MsgBox之外,早期的VB還提供了一系列的函數(shù),比如CStr、CInt、Left、Kill等。為了兼容早期的版本,在VB.NET中仍然提供這些函數(shù),只不過把這些函數(shù)全部放到了Microsoft.Visual- Basic名字空間中。
此名字空間是被VB.NET默認(rèn)引入的,所以可以直接使用其中的函數(shù)。其他的編程語言,比如C#,則必須顯式引入此名字空間(C#使用using語句)后才能使用其中的函數(shù)。
可以把一個(gè)整數(shù)直接傳給MsgBox函數(shù),比如以下代碼是正確并且等價(jià)的:
MsgBox(100)
MsgBox("100")
細(xì)心的讀者一定會(huì)覺得奇怪,數(shù)字與字符串怎么可以作為參數(shù)傳給同一個(gè)函數(shù)?這在以前的VB中是不可以的。這種情況由“重載(Overloads)”這一面向?qū)ο蟮恼Z言特性實(shí)現(xiàn)。
請打開MSDN,查詢MessageBox類的Show()方法,您會(huì)發(fā)現(xiàn)多達(dá)12個(gè)Show()方法的定義,下面列出3個(gè):
Overloads Public Shared Function Show(String) As DialogResult
Overloads Public Shared Function Show(IWin32Window, String) As DialogResult
Overloads Public Shared Function Show(String, String) As DialogResult
……
正是這12個(gè)名字相同的函數(shù),讓軟件工程師可以方便地把不同數(shù)目、不同類型的數(shù)據(jù)傳送給MessageBox.Show()函數(shù),以顯示出相應(yīng)的消息框。
上述12個(gè)函數(shù)即為“函數(shù)重載”特性的表現(xiàn)形式。
那么,構(gòu)成重載情況的函數(shù)擁有哪些特性?請記住以下規(guī)則:
(1)函數(shù)名相同。
(2)參數(shù)類型不同,或參數(shù)個(gè)數(shù)不同。
符合以上特性的函數(shù)構(gòu)成重載關(guān)系。
當(dāng)在代碼中使用重載函數(shù)時(shí),根據(jù)傳入的參數(shù)由計(jì)算機(jī)自動(dòng)選擇最合適的一個(gè)函數(shù)運(yùn)行。
在VB.NET中,除了函數(shù),Sub過程也是可以重載的。以下是一個(gè)實(shí)例(參見配套光盤上的實(shí)例FunctionOverLoads)。
定義四個(gè)重載的IknowWhatYouGiveMe():
'重載的函數(shù)
Public Sub IKnowWhatYouGiveMe()
MsgBox("您什么也沒有給我?。?!")
End Sub
Public Sub IKnowWhatYouGiveMe(ByVal arg As Integer)
MsgBox("您傳入了一個(gè)整數(shù):" & arg)
End Sub
Public Sub IKnowWhatYouGiveMe(ByVal arg As String)
MsgBox("您傳入了一個(gè)字符串:" & arg)
End Sub
Public Sub IKnowWhatYouGiveMe(ByVal arg As Control)
MsgBox("您傳入了一個(gè)控件:" & arg.GetType.Name)
End Sub
在按鈕的單擊事件中可以這樣調(diào)用重載的Sub過程:
1 Private Sub btnUseOverloadsFunc_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnUseOverloadsFunc.Click
2 '調(diào)用重載的Sub過程
3 IKnowWhatYouGiveMe()
4 IKnowWhatYouGiveMe(100)
5 IKnowWhatYouGiveMe("Hello.VB.NET")
6 IKnowWhatYouGiveMe(New TextBox)
7 End Sub
特別注意第4個(gè)重載的IknowWhatYouGiveMe() 過程其參數(shù)是一個(gè)Control對象。類Control是所有可視化窗體控件的祖先,可以把任何一個(gè)由它繼承而來的類對象傳給它。第6句動(dòng)態(tài)創(chuàng)建了一個(gè)文本框?qū)ο笞鳛閰?shù),可以通過Control類對象的GetType方法獲取對象的類型(是Type類的一個(gè)對象),再調(diào)用Type類對象的Name方法獲取其名稱(這一句涉及許多面向?qū)ο蟮闹R,初學(xué)者可以不必深究,在掌握了更多的.NET知識之后就會(huì)明白其中的奧秘)。
 試一試
修改第6句參數(shù),傳入:
(1)一個(gè)標(biāo)簽(Label)對象。
(2)Me關(guān)鍵字。
看看運(yùn)行結(jié)果。
 試一試
編寫一個(gè)重載的Add()方法,用于把兩數(shù)相加,接收整數(shù)和小數(shù)兩種不同的參數(shù)。
2.2.6 字符串使用
字符串可能是編程中用得最多的一種類型了。
以下代碼定義了一個(gè)字符串變量:
Dim str As String
str = "Hello"
上述兩行代碼可以合并為一行:
Dim str As String = "Hello"
字符串變量存入的數(shù)據(jù)以雙引號分隔,如果這些數(shù)據(jù)中本身就包含了雙引號,則要雙寫此雙引號。以下代碼將一句話——他說:“您好!”,存入到字符串變量Str中。
Dim str As String = "他說:""您好!"""
可以使用“+”或“&”運(yùn)算符連接兩個(gè)字符串。
str = "Hello." + "World!"
str = "Hello." & "World!"
上述兩句完全可以相互替換。
有時(shí),可能還需要在字符串中換行,以下代碼將一首詩存入到字符串中并在控制臺(tái)中輸出:
Module Module1
Sub Main()
Dim str As String
str = " 畫" & ControlChars.NewLine
str += "遠(yuǎn)看山有色," & ControlChars.NewLine
str += "近聽水無聲。" & ControlChars.NewLine
str += "春去花還在," & ControlChars.NewLine
str += "人來鳥不驚。"
Console.WriteLine(str)
'屏幕暫停,回車退出
Console.ReadLine()
End Sub
End Module
運(yùn)行結(jié)果如圖2-75所示。
代碼中的ControlChars.NewLine是VB.NET提供的特殊字符常量,表示新行。常用的字符常數(shù)如表2-9所示,所有這些常數(shù)均位于Microsoft.VisualBasic名字空間的Constants模塊中,VB.NET可以直接使用。
2-9 VB.NET的字符常數(shù)
成 員
常 量
等 效
說 明
CrLf
vbCrLf
Chr(13) + Chr(10)
回車/換行組合符
Cr
vbCr
Chr(13)
回車符
Lf
vbLf
Chr(10)
換行符
NewLine
vbNewLine
Chr(13) + Chr(10)
新行符
NullChar
vbNullChar
Chr(0)
值為0的字符
Tab
vbTab
Chr(9)
制表符
Back
vbBack
Chr(8)
退格字符
 提示
上述示例是一種新的項(xiàng)目類型,稱為控制臺(tái)程序,有關(guān)它的知識請參看本節(jié)的擴(kuò)充閱讀資料。
String變量與其他類型的變量連接時(shí),需要進(jìn)行類型轉(zhuǎn)換,可以使用CStr() 函數(shù)完成此功能。以下代碼將整數(shù)轉(zhuǎn)為字符串類型:
Dim str As String
str = "1+100=" + CStr(1 + 100)
字符串由字符組成,可以通過字符串變量的Chars()屬性訪問單個(gè)字符,以下代碼逆序輸出字符串中的每個(gè)字符:
Dim str As String
str = "尺千三下直流飛"
Dim c As Char
Dim i As Integer
For i = str.Length - 1 To 0 Step -1
c = str.Chars(i)
Console.WriteLine(c)
Next
字串中的字符按Unicode進(jìn)行編碼,每個(gè)字符占兩個(gè)字節(jié)。
可以使用String類的SubString方法獲取子字符串,參看以下代碼:
Dim str As String
str = "子在川上曰:逝者如斯夫!"
Dim str1, str2 As String
str1 = str.Substring(0, 6)
str2 = str.Substring(6)
第一句SubString從頭開始,取6個(gè)字符,注意字符位置是從0開始的。運(yùn)行完后,str1=“子在川上曰:”。
第二句從第7個(gè)字符開始,由于省略了第二個(gè)參數(shù),就直接截取到字符串結(jié)束。運(yùn)行完后,str2=“逝者如斯夫!”。
String類對象具有一個(gè)很獨(dú)特的特性,即字符串內(nèi)容的恒定性。
一個(gè)String對象一旦創(chuàng)建,其內(nèi)容就是不可改變的。上述代碼中的SubString方法并不是把Str本身給截短了,而是將Str中的一段取出來,生成一個(gè)新的字符串傳給Str1(和Str2)。
 技術(shù)探索
.NET提供了另外一個(gè)類StringBuilder。與String類不一樣,這個(gè)類定義的變量其本身內(nèi)容是可以改變的。請從MSDN中搜索資料,學(xué)會(huì)使用這個(gè)類。
 擴(kuò)充閱讀
控制臺(tái)應(yīng)用程序簡介
控制臺(tái)程序類似于傳統(tǒng)的DOS程序,它運(yùn)行時(shí)會(huì)打開一個(gè)黑底白字的窗口,以字符的方式顯示程序運(yùn)行結(jié)果??刂婆_(tái)程序一般沒有可視化的主窗體。
在VS .NET中有一個(gè)項(xiàng)目模板可用于創(chuàng)建控制臺(tái)程序,如圖2-76所示。
圖2-76 創(chuàng)建控制臺(tái)程序
與Windows Form程序不一樣,控制臺(tái)程序并不需要?jiǎng)?chuàng)建一個(gè)“主窗體”,它由一個(gè)特殊的Sub Main()過程開始執(zhí)行。此Sub Main()過程通常放在一個(gè)Module(模塊)中(注:Sub Main()過程也可以放在任何一個(gè)類中,但一個(gè)程序只能有一個(gè)Sub Main()過程)。
放在Module中的函數(shù)和Sub過程可以自動(dòng)被項(xiàng)目中的其他類直接使用(不需要也不能創(chuàng)建一個(gè)Module類型的對象)。
因此,可以把Module看成是一個(gè)其內(nèi)部成員全都為共享(Shared)公有成員的特殊類,也許將其比做項(xiàng)目的公有函數(shù)庫更易于理解。
類Console代表控制臺(tái)。它提供了ReadLine、WriteLine等方法以輸入和輸出數(shù)據(jù)。在控制臺(tái)應(yīng)用程序中,此類的輸出通過DOS窗口進(jìn)行。而在Windows Form應(yīng)用程序中,如果使用這些方法輸出信息,則這些信息將顯示在VS .NET的輸出窗口中。如果Windows Form不是通過VS .NET啟動(dòng)的,則用戶將看不見這些輸出的信息。
2.2.7 遞歸
在中學(xué)語文課本中,有一個(gè)著名的“愚公移山”的寓言:愚公認(rèn)為太行與王屋兩座大山擋住了他的路,于是發(fā)動(dòng)全家人上山挖土。愚公對智叟說,雖然他年紀(jì)大了,肯定看不到大山給搬走了,但他有兒子。他的名言可謂流傳千古:“子又生孫,孫又生子,子子孫孫無窮匱也,而山不加增,何苦而不平?”。事情的結(jié)果是愚公的精神感動(dòng)了天帝,他派了兩個(gè)大力神把兩座大山給搬走了。
愚公的故事有著多方面的寓意,有趣的是,愚公這種做事的方式在計(jì)算機(jī)中也可以看到,這就是一種重要的編程方法——函數(shù)或Sub過程自己調(diào)用自己,這種方法稱為遞歸。
理解在軟件技術(shù)中的遞歸概念對于初學(xué)者而言是不容易的,因?yàn)樗c人們常規(guī)的思維方式不同。有趣的是,在藝術(shù)家那里,遞歸往往以一種更易于理解的方式呈現(xiàn),請看如圖 2-77所示的畫。
圖2-77是著名的荷蘭設(shè)計(jì)師M.C.Escher(1898—1972)的作品,在畫中他繪制了一群魚,有趣的是,魚身上的魚鱗又是一條魚……這種“用魚來繪制魚”的表現(xiàn)方法,就是遞歸思想在藝術(shù)中的表現(xiàn)。
回到軟件技術(shù),以下代碼就是非常明顯的遞歸代碼:
Sub DoNotRunMe()
'……
DoNotRunMe()
End Sub
可以看到,這種代碼在執(zhí)行過程中又會(huì)調(diào)用自己,于是又執(zhí)行一次自己,在新的一次調(diào)用中再次重復(fù)這個(gè)過程……這是一個(gè)無窮無盡的過程,理論上講可以運(yùn)行到宇宙末日。這個(gè)過程像不像愚公說的“子又生孫,孫又生子”?所以說,計(jì)算機(jī)可算是當(dāng)代最大的愚公了。
然而,一段永遠(yuǎn)也不會(huì)結(jié)束的代碼是沒有實(shí)際意義的,就是愚公移山,也有它終止的一日,那就是山給移走了。因此,如果在編程中使用遞歸,就一定要給它設(shè)定一個(gè)結(jié)束條件。
愚公移山之所以能夠成功,關(guān)鍵在于“山不加增”,而“子子孫孫無窮匱”,如果山也同步加增,則永遠(yuǎn)也無法移走大山。因此,要使遞歸最終得以結(jié)束,一定要在每次調(diào)用自己時(shí)讓某個(gè)變量(在故事中就是山的高度)隨之變化,當(dāng)其達(dá)到一個(gè)最終界限時(shí)(故事中的山的高度為零),則結(jié)束調(diào)用過程(愚公移山成功)。
現(xiàn)在我們來看如圖2-78所示的有趣實(shí)例(示例項(xiàng)目Recursion,配套光盤中有)。
您一定對這首著名的“兒歌”很熟悉:
從前有座山,山里有座廟。
廟里有兩個(gè)和尚,在講故事。
講什么故事呢?……
從前有座山,山里有座廟。
廟里有兩個(gè)和尚,在講故事。
講什么故事呢?……
這就是一個(gè)很典型的遞歸。示例Recursion用遞歸模擬了這個(gè)小時(shí)候常唱的兒歌。
這個(gè)程序是如何寫出來的?下面介紹一下開發(fā)過程。
在窗體上放了一個(gè)RichTextBox控件用于顯示遞歸的結(jié)果,使用一個(gè)NumericUpDown控件updnTimes來限制遞歸調(diào)用次數(shù)。
實(shí)現(xiàn)代碼如下:
'定義一個(gè)Story變量用于存放故事主體
Private Story As String = ""
'故事主體
Private Sub WriteStory()
Story = "從前有座山,山里有座廟。" & vbCrLf
Story += "廟里有兩個(gè)和尚,在講故事。" & vbCrLf
Story += "講什么故事呢?……" & vbCrLf
End Sub
以下代碼實(shí)現(xiàn)遞歸:
'用于實(shí)現(xiàn)遞歸調(diào)用的Sub過程
Private Sub DoRecursion(ByVal times As Integer)
'結(jié)束條件
If times = 0 Then
Exit Sub
End If
'每次遞歸調(diào)用時(shí)要完成的工作
Me.RichTextBox1.AppendText("第 " & CStr(times) & " 次" & vbCrLf)
Me.RichTextBox1.AppendText(Story)
'遞歸調(diào)用,參數(shù)減1
DoRecursion(times - 1)
End Sub
在這個(gè)遞歸調(diào)用的Sub過程中,參數(shù)times就是用于控制遞歸深度的控制變量,每次調(diào)用時(shí)它減1。減到0時(shí),遞歸結(jié)束。
在按鈕單擊事件中運(yùn)行遞歸過程:
Private Sub btnExecute_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnExecute.Click
'清空文本
Me.RichTextBox1.Text = ""
'老和尚開始沒完沒了地講故事,直到小和尚不耐煩為止
DoRecursion(Me.updnTimes.Value)
End Sub
運(yùn)行的結(jié)果如圖2-78所示。
圖2-78 遞歸的實(shí)例
 試一試
將Sub DoRecursion()中干活的兩句:
Me.RichTextBox1.AppendText("第 " & CStr(times) & " 次" & vbCrLf)
Me.RichTextBox1.AppendText(Story)
移到
DoRecursion(times - 1)
之后,會(huì)發(fā)生什么情況?您能想得清楚嗎?
遞歸最能體現(xiàn)出“計(jì)算機(jī)的思維”與“人的常規(guī)思維”的不同。理解它也是初學(xué)編程者最頭痛的問題之一。然而,一旦您真正理解了它,就會(huì)發(fā)現(xiàn)這種思維方法是很巧妙的。而在實(shí)際開發(fā)中,遞歸是非常常用的編程技巧,在本書的后面,有不少地方使用了遞歸的編程方法。希望讀者好好體會(huì)。
2.2.8 .NET中的集合
在高中代數(shù)中學(xué)過集合的概念,集合是若干有著相同特性的元素的整體。
在程序設(shè)計(jì)中,集合有著非常多的應(yīng)用。本節(jié)介紹.NET中最常使用的兩個(gè)集合數(shù)據(jù)類型:ArrayList和HashTable。
1.ArrayList
ArrayList與數(shù)組非常相像,放在ArrayList中的元素也是通過下標(biāo)訪問的,但與數(shù)組不同的是,ArrayList中的元素可以隨時(shí)增加和刪除,所以,ArrayList又可以被看成是一個(gè)“動(dòng)態(tài)數(shù)組”。
以下是一個(gè)使用實(shí)例(見配套光盤中的UseCollection)。
'使用ArrayList
Private Sub btnUseArrayList_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnUseArrayList.Click
'創(chuàng)建ArrayList對象
Dim arr As New ArrayList
'向ArrayList中增加元素
arr.Add("張三")
arr.Add("李四")
arr.Add("王五")
'顯示ArrayList內(nèi)容
ShowArrayList(arr)
'刪除第二項(xiàng)(注意下標(biāo)從0開始)
arr.RemoveAt(1)
'再次顯示ArrayList內(nèi)容
ShowArrayList(arr)
End Sub
'顯示ArrayList內(nèi)容
Private Sub ShowArrayList(ByVal arr As ArrayList)
Dim i As Integer
For i = 0 To arr.Count - 1
MsgBox("下標(biāo):" & i & " 值:" & arr(i))
Next
End Sub
可以看到集合元素通過下標(biāo)訪問,上述代碼中刪除了ArrayList中間的一個(gè)元素,后面的元素自動(dòng)補(bǔ)上。除了調(diào)用Add方法增加元素,還可以使用Insert和AddRange等方法。同樣ArrayList也提供了多種刪除元素的手段,請讀者自行參閱MSDN。
需要指出的是,ArrayList中可以存放多種數(shù)據(jù),除了例子中的字符串?dāng)?shù)據(jù),也可以是數(shù)字,甚至是一個(gè)窗體變量。但一般而言,最好在ArrayList中存入同一種數(shù)據(jù)類型的信息,使得代碼便于閱讀和排錯(cuò)。
2.HashTable
存放在HashTable中的數(shù)據(jù)是一種“鍵∣值(Key-Value)”形式的數(shù)據(jù)。換句話說,HashTable中的每個(gè)數(shù)據(jù)都有一個(gè)惟一的標(biāo)志。與ArrayList不同,要訪問HashTable中的元素,必須給定一個(gè)鍵(Key)。
以下是一個(gè)實(shí)例(見配套光盤中的UseCollection):
'使用HashTable
Private Sub btnUseHashTable_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnUseHashTable.Click
'創(chuàng)建HashTable對象
Dim hst As New Hashtable
'向HashTable中增加元素
hst.Add("Monday", "星期一")
hst.Add("Tuesday", "星期二")
hst.Add("Wednesday", "星期三")
hst.Add("Thursday", "星期四")
hst.Add("Friday", "星期五")
hst.Add("Saturday", "星期六")
hst.Add("Sunday", "星期日")
'訪問HashTable中的元素
MsgBox("英文:Monday的中文意思是:" & hst("Monday"))
'遍歷HashTable的所有元素
ShowHashTable(hst)
MsgBox("現(xiàn)在刪除了Wednesday")
'刪除星期三
hst.Remove("Wednesday")
MsgBox("開始查看刪除了Wednesday之后HashTable中的內(nèi)容")
'再次遍歷HashTable的所有元素
ShowHashTable(hst)
End Sub
'顯示HashTable內(nèi)容
Private Sub ShowHashTable(ByVal hst As Hashtable)
Dim myDE As DictionaryEntry
For Each myDE In hst
MsgBox("key:" & myDE.Key & " Value:" & myDE.Value & _
" Hash值:" & myDE.Key.GetHashCode())
Next myDE
End Sub
運(yùn)行這個(gè)示例,讀者會(huì)發(fā)現(xiàn)HashTable中的元素與ArrayList中的元素訪問方式是不一樣的,它必須通過一個(gè)給定的Key來訪問。而要遍歷一個(gè)HashTable,其方法也與ArrayList不一樣,必須使用For Each語句。特別要注意在For Each語句中,用到的變量myDE是DictionaryEntry類型的,這是因?yàn)橛捎贖ashtable 的每個(gè)元素都是一個(gè)鍵/值對,因此元素類型既不是鍵的類型,也不是值的類型,而是 DictionaryEntry 類型。
另外,ShowHashTable()過程中用到了一個(gè)GetHashCode()方法,這個(gè)方法由類Object提供,用于返回一個(gè)對象的哈希(Hash)值。所謂哈希值,就是一個(gè)很長的數(shù)字,這個(gè)數(shù)字由特定的數(shù)學(xué)算法(稱為哈希函數(shù))生成。不同的對象擁有不同的哈希值,HashTable就是利用這個(gè)值實(shí)現(xiàn)元素的快速定位的。
 提示
因?yàn)楣:瘮?shù)針對不同的數(shù)據(jù)能生成不同的關(guān)鍵字,因此在海量數(shù)據(jù)檢索中有著重要的應(yīng)用。假設(shè)需要在上億條記錄中查找特定的記錄,如果只能從頭開始一條一條地順序查找,那一定是非常低效的。然而,如果在保存信息時(shí)就是按照這些信息的哈希值進(jìn)行安排的,則不管信息有多少,都可以在一個(gè)相對恒定的時(shí)間內(nèi)得到結(jié)果。
計(jì)算機(jī)專業(yè)課《數(shù)據(jù)結(jié)構(gòu)》中介紹了哈希函數(shù)的設(shè)計(jì)方法以及哈希查找原理。
由于HashTable是通過Key來訪問元素的,這就要求在往HashTable中增加元素時(shí),必須先判斷一下是否已存在此Key。如果硬要往HashTable中增加相同的Key(比如上例中向HashTable中加入兩個(gè)Key為“Sunday”的元素),.NET Framework將拋出一個(gè)異常表明發(fā)生了錯(cuò)誤,如圖2-79所示。
圖2-79 不允許向HashTable中加入相同Key的元素
解決方法是在向HashTable中添加新元素時(shí),先判斷一下Key是否已存在。下面這個(gè)方法可以安全地向HashTable添加元素:
'安全地向HashTable中增加元素的Sub過程
Private Sub AddElementToHashTable(ByVal key As Object, _
ByVal value As Object,_
ByVal hst As Hashtable)
'判斷參數(shù)是否有效
If (key Is Nothing) OrElse (hst Is Nothing) Then
Exit Sub
End If
'是否已存在此Key?
If hst.ContainsKey(key) Then
Exit Sub
End If
'將元素加入到HashTable中
hst.Add(key, value)
End Sub
閱讀AddElementToHashTable()過程時(shí)要注意以下幾點(diǎn):
(1)第一段代碼先檢測參數(shù)是否有效。這段代碼看似多余,但卻能保證后面的代碼運(yùn)行正常,在開發(fā)大規(guī)模的軟件時(shí)是非常必要的,這種編碼方式稱為“防衛(wèi)性編碼”。其中的OrElse是VB.NET提供的關(guān)鍵字,可以用Or代替,其差別在于使用Or時(shí),其兩邊的表達(dá)式必須都被計(jì)算出來后才可以得出整個(gè)邏輯表達(dá)式的值,而使用OrElse時(shí),只要有一個(gè)表達(dá)式計(jì)算后確認(rèn)其結(jié)果為True,就不再計(jì)算后面的表達(dá)式,這就意味著程序使用OrElse會(huì)比Or運(yùn)行得快一點(diǎn)。
 提示
與Or類似,And也有對應(yīng)的一個(gè)“AndAlso”關(guān)鍵字,只要它所連接的表達(dá)式中發(fā)現(xiàn)有一個(gè)結(jié)果為False,就不會(huì)再計(jì)算后面的表達(dá)式值。請讀者自行查閱MSDN了解詳情。
(2)判斷某個(gè)Key是否已存在是通過HashTable對象的ContainsKey()方法實(shí)現(xiàn)的。
(3)Exit Sub用于提前退出Sub過程,后面的代碼不會(huì)再執(zhí)行。
將原來代碼中調(diào)用Add()方法向HashTable中添加元素改為使用AddElementToHashTable(),就可以放心地向HashTable中增加元素而不會(huì)出錯(cuò)了。
3.何時(shí)使用ArrayList和HashTable
本節(jié)中介紹了兩種典型的集合:ArrayList和HashTable,那么在具體編程時(shí)如何選用呢?
一般而言,如果需要按下標(biāo)訪問元素,則使用ArrayList,ArrayList可以起到與數(shù)組一樣的作用,而其使用要比數(shù)組方便得多。如果經(jīng)常需要查找特定的元素,則HashTable要好得多。
除了這兩個(gè)常用的集合,.NET Framework還提供了其他的集合類,如Stack,Queue,SortedList等,這些類都位于System.Collections名字空間中。還有一些特殊的集合類,如ListDictionary,StringCollection等,放在System.Collections.Specialized名字空間中。
 技術(shù)探索
請通過查詢System.Collections和System.Collections.Specialized名字空間了解.NET Framework提供了哪些集合類,以及這些集合類的特點(diǎn)。
2.2.9 VB.NET開發(fā)實(shí)踐:多窗體編程
在前面介紹的程序中,大都只有一個(gè)窗體。但在實(shí)際的程序中,往往會(huì)有多個(gè)窗體,并且這些窗體之間還有著復(fù)雜的關(guān)系。掌握多窗體編程的技巧對于開發(fā)真正的軟件系統(tǒng)是非常重要的。
1.在一個(gè)窗體中訪問另一個(gè)窗體的控件
新建一個(gè)Windows應(yīng)用程序VisitOtherForm(示例源代碼參見配套光盤),刪除其中的Form1,新建兩個(gè)Form:主窗體frmMain和輔助窗體frmOther。
在主窗體上放置兩個(gè)按鈕,btnShowOtherForm和btnGetUerInputText,分別用于顯示輔助窗體和獲取輔助窗體中用戶的輸入,還有一個(gè)標(biāo)簽lblUserInput,用來顯示輔助窗體中輸入的信息,如圖2-80所示。
輔助窗體中有一個(gè)文本框txtUserInput,可用于輸入信息,如圖2-81所示。
 
0 設(shè)計(jì)主窗體圖2-81 設(shè)計(jì)輔助窗體
這個(gè)實(shí)例運(yùn)行的結(jié)果如圖2-82所示。
2 實(shí)例運(yùn)行效果
分析一下主窗體與輔助窗體之間的信息傳送是如何實(shí)現(xiàn)的。
在主窗體中定義了一個(gè)窗體級的變量:
'定義輔助窗體的變量
Private frm As frmOther = Nothing
在btnShowOtherForm按鈕單擊事件中編寫代碼顯示輔助窗體:
Private Sub btnShowOtherForm_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnShowOtherForm.Click
If frm Is Nothing Then '輔助窗體還沒有創(chuàng)建
frm = New frmOther
End If
'顯示輔助窗體
frm.Show()
End Sub
注意一下If條件語句,在前面定義frm變量時(shí)它被初始化為Nothing,因此可以通過這點(diǎn)來判斷是否已創(chuàng)建了一個(gè)窗體對象。如果沒有創(chuàng)建就直接顯示此窗體,會(huì)引發(fā)一個(gè)NullException- Exception異常錯(cuò)誤,這種異常在前面已經(jīng)見過了。
那為何不直接創(chuàng)建窗體然后顯示它,非得要先判斷一下呢?這是因?yàn)橛脩艨赡芏啻螁螕暨@個(gè)按鈕,如果每次都新建一個(gè)窗體,則屏幕上會(huì)有多個(gè)輔助窗體,只有最后出現(xiàn)的那個(gè)輔助窗體的用戶輸入才能被主窗體獲取。
 試一試
注釋掉If和End If語句,保留當(dāng)中的New語句,編譯運(yùn)行看看結(jié)果。
在btnGetUerInputText按鈕單擊事件處理程序中書寫代碼,獲取用戶在輔助窗體中輸入的信息:
Private Sub btnGetUerInputText_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnGetUerInputText.Click
'如果輔助窗體還沒創(chuàng)建,則退出此Sub過程
If frm Is Nothing Then
Exit Sub
End If
'獲取輔助窗體的控件內(nèi)容
Me.lblUserInput.Text = frm.txtUserInput.Text
End Sub
可以看到,其中的關(guān)鍵部分只有一句:
Me.lblUserInput.Text = frm.txtUserInput.Text
可以通過frm變量來直接訪問另一窗體上控件的內(nèi)容。
 注意
上述代碼在同一個(gè)項(xiàng)目內(nèi)部是可以正常工作的,但如果兩個(gè)窗體分屬于不同的Assembly(即程序集),則必須把輔助窗體中文本框txtUserInput的Modifier屬性由Friendly改為Public。
2.使用自定義屬性在窗體間相互傳送信息
上面的例子通過在主窗體中直接訪問輔助窗體的控件實(shí)現(xiàn)了窗體間信息的傳送,但這需要將輔助窗體的內(nèi)部數(shù)據(jù)暴露給外界,違反了面向?qū)ο蟮脑O(shè)計(jì)原則。較好的方法是給窗體添加自定義的公有屬性,外界通過給這些屬性賦值實(shí)現(xiàn)信息傳送。
請看圖2-83所示的示例(參見配套光盤中的UsePropertyBetweenForm項(xiàng)目)。
圖2-83 UsePropertyBetweenForm實(shí)例運(yùn)行效果
當(dāng)實(shí)例運(yùn)行時(shí),每單擊一次按鈕,就會(huì)創(chuàng)建一個(gè)輔助窗體的對象,在其標(biāo)簽中顯示一條信息,并且修改每個(gè)窗體的標(biāo)題。
與上一個(gè)例子直接訪問輔助窗體的Label控件不同,主窗體向輔助窗體傳送的信息是通過給輔助窗體的Title自定義屬性賦值來實(shí)現(xiàn)的。
另外,如何在程序中跟蹤窗體創(chuàng)建的個(gè)數(shù)?這是通過在輔助窗體中設(shè)置一個(gè)共享的公有變量實(shí)現(xiàn)的:
'窗體計(jì)數(shù)器
Public Shared counter As Integer = 0
在什么地方增加計(jì)數(shù)?因?yàn)閯?chuàng)建一個(gè)對象時(shí),會(huì)自動(dòng)調(diào)用它的構(gòu)造過程N(yùn)ew(),所以,在Sub New()過程中完成這件事是最方便的了。
Public Sub New()
MyBase.New()
'該調(diào)用是 Windows 窗體設(shè)計(jì)器所必需的
InitializeComponent()
'在 InitializeComponent() 調(diào)用之后添加任何初始化
counter += 1 '窗體計(jì)數(shù)器加1
End Sub
輔助窗體的Title屬性是一個(gè)自定義的屬性:
'定義一個(gè)自定義的屬性
Public Property Title() As String
Get
Return Me.lblInfo.Text
End Get
Set(ByVal Value As String)
Me.lblInfo.Text = Value
End Set
End Property
只要給這個(gè)自定義屬性賦值,其值就會(huì)自動(dòng)地用標(biāo)簽顯示出來,外界調(diào)用者甚至不知道輔助窗體中有一個(gè)標(biāo)簽存在。同時(shí),如果需要對所賦的值進(jìn)行一些預(yù)處理(例如將小寫字母全部改為大寫字母),或者是這一賦值會(huì)同時(shí)影響多個(gè)控件,都可以直接在屬性的Set過程中實(shí)現(xiàn)。由于有這些方便性,因此,通過自定義屬性在窗體間傳送信息是推薦的方式。
主窗體中的代碼就比較簡單了:
Private Sub btnCreateForm_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnCreateForm.Click
'創(chuàng)建輔助窗體對象
frm = New frmOther '注意在frmOther的構(gòu)造函數(shù)中自動(dòng)對創(chuàng)建對象的個(gè)數(shù)計(jì)數(shù)
'訪問窗體計(jì)數(shù)器,并顯示在窗體標(biāo)題欄上
'counter是Shared變量,通過 類名.變量名 來訪問
frm.Text = "第 " & frmOther.counter & " 個(gè)窗體"
'通過向自定義屬性 Title 賦值實(shí)現(xiàn)信息的傳送
frm.Title = "主窗體向輔助窗體傳送的第 " & frmOther.counter & " 條信息"
'顯示輔助窗體
frm.Show()
End Sub
 技術(shù)探索
上面所介紹的兩個(gè)例子都是從主窗體向輔助窗體傳送或查詢信息,是單向的,能不能實(shí)現(xiàn)雙向的傳送,即輔助窗體也能主動(dòng)地向主窗體傳送信息?
在看下面的提示之前,自己想一想,怎么實(shí)現(xiàn)?
 提示
可以在輔助窗體中增加一個(gè)主窗體類型的變量,在主窗體創(chuàng)建輔助窗體對象時(shí),把對主窗體對象的引用——“Me”賦值給此變量。
這個(gè)實(shí)例就留給讀者去設(shè)計(jì)并實(shí)現(xiàn)吧。請務(wù)必完成這一練習(xí),以真正理解信息是如何在對象之間相互傳送的。
3.小結(jié)
至此已介紹了許多VB.NET編程的知識,這些知識都是精選出來的,是開發(fā)Windows Form應(yīng)用程序所必須掌握的知識。多窗體編程還有許多技巧,我們將在第3章學(xué)習(xí)更多VB.NET知識之后再介紹。
在下面的一節(jié)中將把學(xué)到的知識應(yīng)用于實(shí)際,開發(fā)出一個(gè)功能強(qiáng)大的文本編輯器,這將是讀者所接觸到的第一個(gè)真正實(shí)用的Windows Form應(yīng)用程序。
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
VBA新手入門篇 - excel函數(shù),excel公式,excel學(xué)習(xí),excel基礎(chǔ),ex...
VB基礎(chǔ)教程
[《CC 學(xué)習(xí)指南》語法篇(零基礎(chǔ)入門到精通)]-百度傳課
Java與C、C++的區(qū)別
史上最全!一文讓你學(xué)完C ,干貨收藏?。?!
編程規(guī)范
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服