如果你以前經常使用visual basic的話,你會發現visualbasic .net有點熟悉,又有一點陌生。在可以利用許多以前就具備的技巧和知識的同時,仍然有很多東西要學。
許多公司花費了大筆的資金投資于visual basic6.0編寫的軟件,這些公司將面臨如何處理這些已存在的軟件的問題。我在介紹visual basic .net新特點的同時,將會介紹影響把代碼移植到visual basic .net的關鍵所在。正如你將看到的,會有很多問題要考慮。隨著編程語言變得更加一致、強大和友好,你會發現把visual basic 6.0項目改寫成visual basic .net項目需要很大的努力和技術。如果順利的話,你將有幸在不遠的將來從零開始編寫一個visualbasic .net的項目。
新的便利:
我首先將介紹用一行代碼實現聲明并初始化變量的新語法。盡管許多初學者會認為這種語法是理所當然的,但實際上那些已習慣于在visual basic用兩行代碼來聲明和初始化變量的程序員會對此感到很欣慰。
這兒是利用了visual basic .net語法優勢的三個例子:
dim x as integer = 10
dim obj1 as class1 = new class1
dim obj2 as new class2
請注意,與前兩行不一樣,最后一行用的是在visual basic 6.0中合法的語法,它在visual basic .net中同樣是合法的。然而,值得注意的是,這兒的as new語句在visual basic .net and visual basic 6.0中的處理是不同的,許多有經驗的程序員反對使用as new語句,因為它將導致初始化的延遲,進而導致較低的執行效率,并給調試程序帶來了許多困難。
好消息是as new語法在visual basic .net中并不會造成初始化的延遲,因此不會導致相同的問題。在看前面的例子時,你應該注意第三行,它用了as new語句,但與前面幾行具有一樣的執行效率。當你在visual basic .net中使用as new語句時,這個對象將在執行下一行之前被創建、初始化,并指派給你的變量。
這種方便的初始化語法也可以被用在類或結構的定義中。正如你想得那樣,語法應該是這樣的:
class class1
private field1 as integer = 10
public field2 as class1 = new class1
end class
另一點值得注意的是,當你在同一行聲明幾個變量時,他們應該是相同類型的變量,請看這個例子:
dim x, y, z as integer
三個變量都被聲明成integer類型,你不再需要擔心前兩個變量偶然會被當作variant類型。實際上,你不用擔心有什么變量會被當作variant類型處理,因為variant類型不再被clr編程模型所支持。現在的通用類型是system.object.
在visual basic .net新增加的語法中,我最喜歡的一點是現在函數可以用return來向它的調用者返回一個值。請看下面的例子:
function myfunction() as string
return "this is my return value"
end function
這個例子和用其他語言(比如c語言)一樣,return語句將結束函數的執行,并把控制權返回給調用者。我覺得使用return語句與我們在visualbasic的早期版本中的做法相比要方便得多。正如你記得的,在以前的版本中,要返回給調用者的值必須在函數的內部被賦給函數名。由于在visualbasic .net中使用了return語句,你可以很容易地實現更改函數名或把代碼從一個函數中復制、粘貼到另一個函數,而不需要搜索函數的內容并替換原來的函數名。
消除矛盾
正如我在二月份的文章中提到的那樣,在把一個對象指派給一個變量時,visual basic .net既不需要,也不允許你用set這個關鍵字。visualbasic 6.0 和visual basic .net的編譯器有許多方面不一樣,這就是其中之一。請看下面的代碼:
dim obj1, obj2, obj3, obj4 as class1
obj1 = new class1
obj2 = obj1
set obj3 = new class1
set obj4 = obj3
第二行和第三行代碼在visual basic .net中是合法的。但在visualbasic 6.0中編譯時,它們將導致運行期錯誤。第四行和第五行的代碼在visualbasic 6.0中是合法的,但在visual basic .net中卻不能通過編譯。正如你想象的,在把代碼從visualbasic 6.0移植到visual basic.net時,需要特別注意對set語句的改寫。
在用visual basic .net編寫了六個多月的程序后,我發現自己仍然會使用set語句,老習慣是不容易被改掉的。而且由于工作的關系,有好幾次我得在同一天即用visualbasic.net,又用visual basic 6.0編寫程序。不停地來回往復真地很困難。
在參數傳遞中括號的使用也發生了變化,這也給代碼移植增加了困難。你們中的許多人在經過多年的編程之后已經習慣了調用函數和子程序的規則和特點。visualbasic.net小組覺得應該實現調用方法的一致性。
在visual basic. net中,參數傳遞的規則很簡單。當你調用一個函數或子程序并需要傳遞一個或多個參數時,必須用小括號把參數括起來。當調用一個不需要參數的函數或子程序時,小括號可以被省略。你不得不承認,與早期版本的visualbasic中的規則相比,這些規則更直截了當,也更容易去學。
值得注意另一點是參數傳遞的默認方式也改變了。比如,當一個方法被定義成下面這種樣式時,參數是如何被傳給調用者的。
sub method1(param1 as integer)
' implementation
end sub
在visual basic以前的版本中,param1是引用傳遞的。也就是說在執行method1時,改變這個integer類型的參數將導致調用者中相應的實際參數的值的改變。而在visualbasic.net中,默認的參數傳遞方式變成了值傳遞。也就是說執行method1時,只是值被傳遞給了param1,并且在method1中改變parame1的值不會改變調用者中相應的實際參數的值。很明顯,在把visualbasic 6.0中代碼移植到visual basic.net,這種參數傳遞默認方式的改變將破壞一些代碼的語義。
請注意,在上一個例子中,被傳遞到參數的是一個值而不是地址。基本類型比如byte,integer,double, and boolean都是值類型。用戶定義的枚舉和結構類型也是值類型。在以值傳遞方式傳遞時,變量總是被復制。
當你傳遞的參數是基于類時,情況就不同了。基于類的參數是引用類型而不是值類型。它們被用來傳遞對象的引用。如果一個調用者傳遞了一個對象的引用給一個用了值傳遞方式的方法,在這個方法的實現中,對象的狀態可能被改變。這種狀態的改變將反映到調用者中。因此,無論你把參數聲明成值傳遞還是引用傳遞,注意值類型還是引用類型之間的區別是很重要的。
盡管在方法中仍然可以使用可選參數,這仍是值得注意的地方。與visualbasic 6.0不同,使用可選參數需要一個默認值。在同一行中,visual basic.net不支持ismissing函數,也就是說你區分不出一個省略了參數的調用和一個給參數傳了默認值的調用。
如果你考慮在方法中使用可選參數,你應該考慮重載方法。一旦你知道了重載方法的好處和語法,你也許就會停止在方法中使用可選參數。
更高級別的類型保護
許多使用visual basic的開發者希望在每一個visualbasic的源文件的開頭能有一個option explicit語句。option explicit的應用可以區別對待真正的開發者和臨時用戶。和以前的版本不同,visual basic.net缺省就使用option explicit。
在編譯時默認使用option strict是visual basic.net的另一個特點。使用了option strict后,縮小轉換就被禁止了,因為它可能會導致數據或精度的丟失。請看下面的例子:
dim x as double
x = 20.1
dim y as integer
' implicit narrowing conversion
y = x ' doesn't compile under option strict
這段代碼在visual basic 6.0可以被編譯通過,但在visual basic.net中,如果option strict語句被使用,它就不能被編譯通過。option strict的使用使得程序員必須對應該使用何種類型很清楚。
dim x as double
x = 20.1
dim y as integer
' explicit narrowing conversion
y = cint(x) ' compiles under option strict
另一個很大的變化是現在if語句被優化了。這代表了一個很有價值的最優化,而且現在當把代碼移植到visual basic.net時,理解這將如何影響基于visual basic 6.0的代碼變得很重要。請看下面的代碼:
if function1() and function2() then
' statement block
end if
如果function1返回了false值,那需要執行function2以決定是否要執行ifthen中的代碼嗎?很明顯這是不需要的。然而早期版本的visual basic并沒有像這樣優化if語句,在任何情況下function2都將被執行。visualbasic.net實現了優化,因此當function1返回值為false時,function2不會被執行。這是另一個使得基于visualbasic 6.0的代碼不能在visual basic.net中正確執行的因素。
現在我想談談這個語言中在類型安全方面的另一個改進。你應該注意到,如果function1或function2返回的值不是boolean,那么前面的代碼不能通過編譯。由于類型安全的級別更高了,你不可以用integer類型的值進行條件測試了。比如下面的這段代碼,在visual basic 6.0中是被經常使用的,但在visual basic.net中卻不能通過編譯。
dim x as integer
' set x to 0 for false or anything else for true
if x then
' execute statement block
end if
這種由option strict帶來的新的類型安全級別使得你在使用if和doloops語句時,必須使用boolean類型的值,而不能用其它類型的值。
在使用諸如not、 and、 or、 xor等的邏輯比較符時,你也被限制必須使用boolean類型的值來輸入輸出。這與在以前版本的visualbasic中這些比較符既可以用于邏輯比較又可以用于按位比較不同。下面這段代碼在visualbasic 6.0中能正常運行,但在visual basic .net中卻不能通過編譯。
dim x as integer
dim y as integer
dim z as integer
x = 3
y = 6
z = x and y ' doesn't compile
visual basic.net提供了四種新的按位比較運算符:bitnot,bitand, bitor, 和bitxor。這些運算符允許你在類型安全的方式下實行按位比較運算。比如,你可以用這些運算符中的一個對兩個integer值的每一位來實現or運算,還可以實現位掩碼。下面是使用bitor和bitand的一個例子:
dim x as integer = 3
dim y as integer = 6
dim z as integer
' or bits together
z = x bitor y ' z now equals 7
' and to find intersection of bits
z = x bitand y ' z now equal 2
設計數組
聲明和使用數組的基本語法發生了很大的變化。首先,數組的下界別成了零。你不可以聲明一個下界為1的數組。因此,optionbase語句不再被visualbasic .net支持。
另外,你在聲明一個數組時必須用它的元素個數,而不是它的上界來初始化。這兒是一個例子:
' declare an array with 3 elements from 0 to 2
dim array1(3) as integer
array1(0) = 2
array1(1) = 4
array1(2) = 8
上一段聲明如果在visual basic 6.0中被使用,這個數組將有四個元素,下標從0到3。而在visualbasic.net中,這個數組有三個元素,下標從0到2。在visualbasic .net中,如果你的代碼企圖訪問下標為3的數組元素,將引起運行期例外。比如:
' index out of range exception
array1(3) = 16
visual basic.net為初始化數組提供了一種新的語法。你可以只用一行代碼完成數組的聲明和初始化。就像這樣:
new array initialization syntax
dim array1 as integer() = {2, 4, 8}
'
在visual basic的早期版本中,你可以用for each循環遍歷一個數組。
dim x as integer
for each x in array1
console.writeline(x)
next
同時,你也可以用for循環和數組長度來遍歷一個數組。比如:
dim i as integer
for i = 0 to (array1.length - 1)
console.writeline(array1(i))
next i
clr 和visual basic.net還支持多維數組。比如,當你想要一個二維數組時,你可以用下面三個技術中的一個來定義它:
dim array1(2, 2) as integer
dim array2 as integer(,)
redim array2(2, 2)
dim array3 as integer(,) = { {12, 24}, {10, 20} }
使用數組時,你應該知道,不僅僅是語法變了,在運行時被處理的方式也發生了變化。在clr中,數組被分配的地址空間總是在堆中。當你向一個方法傳遞數組類型的參數時,你使用的引用傳遞而不是值傳遞。
這兒是互相傳遞數組引用的三個方法:
dim array1(2, 2) as integer
dim array2 as integer(,)
redim array2(2, 2)
dim array3 as integer(,) = { {12, 24}, {10, 20} }
method1在兩個方向同時傳遞了數組引用,這一般用來向調用者返回數組引用。
method2 和 method3 從調用者向方法的實現中傳遞了數組引用。請注意method2的參數被聲明一維數組,而在method3中參數被聲明成了二維數組。
每一個數組對象繼承了系統支持的類system.array的核心細節實現和公共成員。你可以參考在.net platform sdk中有關system.array的文檔。這個類提供了面向一般數組的任務的函數,比如清空、復制、克隆、搜索和排序。這兒是使用system.array類提供的一些方法的例子:
dim array1 as integer() = {4, 2, 8, 1}
dim array2 as integer()
' cloning an array
array2 = ctype(array1.clone(), integer())
' clearing an array
system.array.clear(array1, 0, array1.length)
' sorting an array
system.array.sort(array2)
' sorting an array in reverse order
system.array.reverse(array2)
請注意,你可以對任何基于icomparable界面實現的數組使用排序操作。clr的核心類型,比如integer,double, string, and date,都被設計成實現icomparable的了。如果你想把數組按你想要的方式進行排序,你只要在類和結構中實現icomparable接口就可以了。
屬性的新語法:
我相信你對在早期版本的visual basic如何定義和使用屬性很熟悉。盡管使用屬性的原因是相同的,但定義屬性的語法已經變了。屬性現在必須用包括set塊或get塊的屬性構造器來定義。
下面是一個有private字段和public屬性的類的例子:
class class1
' private field
private m_name as string
' public property
public property name as string
get
return m_name
end get
set
if value <> "" then ' value is intrinsic variable
m_name = value
end if
end set
end property
end class
請注意set塊中包含了內部變量value。visualbasic.net用這個內部變量向你在set塊中的屬性實現傳遞了由客戶制定的屬性值。另外,在visual basic.net中,程序員不再會有何時應使用set屬性,而何時應使用let屬性的困惑了。
一個屬性一定是readwrite, readonly, 或 writeonly的。上面的例子演示了readwrite的屬性。下面的例子將演示如何創建readonly和 writeonly的屬性。
' readonly property has get but no set
public readonly property fullname as string
get
return m_firstname & " " &m_lastname
end get
end property
' writeonly property has set but no get
public writeonly property password as string
set
m_password = value
end set
end property
請注意,當你省略了get塊或set塊時,你一定要使用readonly或writeonly關鍵字
索引屬性和默認屬性
一個屬性可以被指定一個或多個索引。這可以使屬性具有數組的特點。請看下面的類:
class class1
private m_names as string() = {"ted", "fred", "jed"}
' an indexed property
readonly property item(index as integer) as string
get
return m_names(index)
end get
end property
end class
在客戶端,你可以用下面的例子訪問item屬性。
dim obj as new class1
dim s1 string
s1 = obj.item(0)
如果把這個例子作進一步的改進,你可以把一個索引屬性標記為類的默認屬性。為了實現這一點,只要把default關鍵字加到上一個例子中就可以了,就像這樣:
default readonly property item(index as integer)…
一旦你為一個索引屬性標記了default關鍵字,客戶端的代碼就可以省略屬性的名字而使用對象引用,就像使用一個數組一樣。
dim obj as new class1
dim s1 string
s1 = obj(0)
請注意,對于默認屬性有一個重要的限制,即非索引屬性不可以被標記為默認屬性。這個限制在visual basic.net中體現了出來,因為你不可以用set語句來指定一個對象引用。既然set語句不再被visual basic.net所支持,那么非索引的默認屬性將導致無法解決的多義性問題。
小結
正如你所見到的那樣,在許多方面visual basic.net都與它以前的版本不同。它具有更高的一致性和類型安全級別。另外,用visual basic.net我們更容易寫出控制性、可讀性很高的代碼。雖然它有時要求你習慣于那些編譯時額外的檢查,但它將在你測試和調試時,為你節約寶貴的時間。
好消息是visual basic已經被改成了一種更好、更強有力的語言。而壞消息是在visual basic 6.0下編寫的軟件需要做很多工作才可以被移植到visual basic.net下。