自 1991 年 Visual Basic 語(yǔ)言誕生之日起,它就一直是生成應(yīng)用程序的高效率工具。將近 20 年之后,它繼續(xù)提供與 Microsoft .NET Framework 的輕松對(duì)接,使開(kāi)發(fā)人員能夠編寫(xiě)可跨越桌面、電話(huà)、瀏覽器甚至云的應(yīng)用程序。
Microsoft 將在本月發(fā)布 Visual Studio 2010,其中包含 Visual Basic 版本 10(有時(shí)稱(chēng)為 VB 2010 或 VB10)。此版本是迄今為止最強(qiáng)大的版本,包含許多省時(shí)省力的功能,可以幫助開(kāi)發(fā)人員通過(guò)更少的代碼行完成更多的操作。在這里,將會(huì)為您提供所有必要的內(nèi)容,讓您充分了解并利用 Visual Studio 2010 中的 Visual Basic。
共同演變
在過(guò)去,Visual Basic 和 C# 是由獨(dú)立團(tuán)隊(duì)分別開(kāi)發(fā)的,這通常會(huì)導(dǎo)致一些功能先出現(xiàn)在一種語(yǔ)言中,繼而又出現(xiàn)在另一種語(yǔ)言中。例如,C# 有 Visual Basic 中所沒(méi)有的自動(dòng)實(shí)現(xiàn)屬性和集合初始值設(shè)定項(xiàng),而 Visual Basic 則有 C# 中所沒(méi)有的晚期綁定和可選參數(shù)等功能。但每當(dāng)一種語(yǔ)言具有新功能時(shí),許多客戶(hù)都會(huì)要求將該功能也添加到另一種語(yǔ)言中。
為了解決這一需求,Microsoft 合并了 Visual Basic 和 C# 團(tuán)隊(duì),實(shí)行共同演變的策略。目的是為推動(dòng)這些語(yǔ)言共同發(fā)展。當(dāng)一種語(yǔ)言中引入重大功能時(shí),它也會(huì)出現(xiàn)在另一種語(yǔ)言中。這并不是說(shuō)每種功能都將出現(xiàn)在兩種語(yǔ)言中,并按完全相同的方式工作;實(shí)際上,每種語(yǔ)言都有自己的歷史、靈魂和感覺(jué) – 保留這些特性非常重要。
在 .NET Framework 4 中,Visual Basic 和 C# 朝這一目標(biāo)邁進(jìn)了一大步,分別吸收了對(duì)方既有的許多功能。然而,共同演變不僅影響到以前的功能;它同樣是這些語(yǔ)言未來(lái)的發(fā)展策略。本著這種精神,.NET Framework 4 在兩種語(yǔ)言中同時(shí)引入了強(qiáng)大的新功能,例如動(dòng)態(tài)語(yǔ)言運(yùn)行時(shí)、嵌入式互操作類(lèi)型和泛型方差,從而使 Visual Basic 和 C# 開(kāi)發(fā)人員能夠充分利用 .NET Framework。
Visual Basic 2010 新增功能
Visual Basic 2010 中的新功能旨在幫助您通過(guò)更少的代碼行實(shí)現(xiàn)更多操作。我們 Visual Basic 設(shè)計(jì)團(tuán)隊(duì)仔細(xì)研究了開(kāi)發(fā)人員通常不得不編寫(xiě)大量繁瑣樣板代碼的地方,并找到相應(yīng)解決辦法,讓編譯器代替執(zhí)行此類(lèi)工作。當(dāng)然,這是從整體上來(lái)看,現(xiàn)在就讓我們深入了解各項(xiàng)功能。
隱式行繼續(xù)符
Visual Basic 是一種面向行的語(yǔ)言,它使用類(lèi)似于英語(yǔ)的清晰語(yǔ)法來(lái)增強(qiáng)可讀性。但這通常會(huì)導(dǎo)致代碼遇到每行 80 個(gè)字符的限制,從而迫使開(kāi)發(fā)人員要進(jìn)行大量滾動(dòng)。您可以使用下劃線(xiàn)字符來(lái)告知編譯器應(yīng)將下一行作為當(dāng)前行繼續(xù)處理(也就是說(shuō),將多個(gè)物理行視為單個(gè)邏輯行)。但不得不重復(fù)地鍵入下劃線(xiàn)字符一直很令人煩惱,而事實(shí)上多年以來(lái)排在首位的功能請(qǐng)求就是讓編譯器“解決這個(gè)問(wèn)題”。
而在 Visual Basic 2010 中,編譯器能夠解決這個(gè)問(wèn)題。編譯器現(xiàn)在知道哪些標(biāo)記(例如逗號(hào)、圓括號(hào)和運(yùn)算符)往往出現(xiàn)在行繼續(xù)符前面,并且它會(huì)插入字符,因此開(kāi)發(fā)人員不再需要插入字符。例如,用逗號(hào)作為 Visual Basic 語(yǔ)句的結(jié)尾肯定不合邏輯;編譯器知道這一點(diǎn),因此,當(dāng)編譯器看到諸如 {comma, enter} 這樣的標(biāo)記流時(shí),它會(huì)推斷出存在行繼續(xù)符,如圖 1 中的示例所示。
圖 1 推斷出行繼續(xù)符
<Extension()>
Function FilterByCountry(
ByVal customers As IEnumerable(Of Customer),
ByVal country As String) As IEnumerable(Of Customer)
Dim query =
From c In customers
Where c.Country = country
Select <Customer>
<%=
c.Name &
"," &
c.Country
%>
</Customer>
Return query
End Function
在 Visual Basic 2008 中,圖 1 中的代碼將需要 9 個(gè)下劃線(xiàn)字符。然而,在以下每種情況下,編譯器會(huì)推斷出下劃線(xiàn)字符在何時(shí)是必要的,并允許將其忽略:
在 <Extension()> 屬性之后
在方法聲明中的 ((左圓括號(hào))之后
在第一個(gè)參數(shù)的 ,(逗號(hào))之后
在方法聲明中的 )(右圓括號(hào))之前
在 =(等號(hào))之后
在 <%=(嵌入式表達(dá)式的開(kāi)始標(biāo)記)之后
在 XML 文本的每個(gè) &(與號(hào))之后
在 %>(嵌入式表達(dá)式的結(jié)束標(biāo)記)之前
這個(gè)新的編譯器功能對(duì)于方法簽名特別有用,它對(duì)于所示示例中超過(guò) 80 個(gè)字符的情況也將正常工作(如果每一部分都位于同一行上)。在圖 2 中,您將看到行繼續(xù)符為隱式的標(biāo)記和位置的所有組合。
圖 2 行繼續(xù)符為隱式的情況
| 標(biāo)記 | 之前 | 之后 |
| ,(逗號(hào))、.(句點(diǎn))、>(屬性)、( {(左括號(hào))、<%=(嵌入式表達(dá)式開(kāi)始標(biāo)記(XML 文本)) | X | |
| )、}、](右括號(hào))、%>(嵌入式表達(dá)式結(jié)束標(biāo)記) | X | |
| 所有 LINQ 關(guān)鍵字:
Aggregate、 Distinct、From、Group By、Group Join、Join、Let、Order By、Select、Skip、Skip While、Take、Take While、Where、In、Into、On、Ascending、Descending |
X | X |
| 運(yùn)算符:
+、 -、*、/、/、^、>>、<<、Mod、&、+=、-=、*=、/=、/=、^=、>>=、<& lt;=、&=、<、<=、>、>=、<>、Is、IsNot、Like、And、Or、Xor、 AndAlso、OrElse |
X | |
| With(在對(duì)象初始值設(shè)定項(xiàng)中) | X |
如您所見(jiàn),有 60 多處該語(yǔ)言不需要下劃線(xiàn)字符的地方。(事實(shí)上,本文中的任何一個(gè)代碼示例都不需要行繼續(xù)符。)當(dāng)然,您仍然可以使用下劃線(xiàn)字符,因此 Visual Basic 以前版本中的代碼將仍然按預(yù)期方式編譯。
語(yǔ)句 Lambda
術(shù)語(yǔ) lambda 乍聽(tīng)上去可能很?chē)樔?,?lambda 只是在另一個(gè)函數(shù)內(nèi)定義的函數(shù)。Visual Basic 2008 引入了帶 Function 關(guān)鍵字的 lambda 表達(dá)式:
Dim customers As Customer() = ...
Array.FindAll(customers, Function(c) c.Country = "Canada")
Lambda 表達(dá)式使您能夠在本地以細(xì)致緊湊的方式表達(dá)邏輯,而不必跨多個(gè)方法拆分邏輯。例如,下面是 Visual Basic 2005(不支持 lambda 表達(dá)式)中以前的代碼的表示形式:
Dim query = Array.FindAll(customers, AddressOf Filter)
...
Function Filter(ByVal c As customer) As Boolean
Return c.Country = "Canada"
End Function
不幸的是,Visual Basic 2008 的 lambda 表達(dá)式要求表達(dá)式返回值,因此以下代碼:
Array.ForEach(customers, Function(c) Console.WriteLine(c.Country))
將會(huì)導(dǎo)致以下情況:
'Compile error: "Expression does not produce a value."
Console.WriteLine 是一個(gè) Sub 過(guò)程(C# 中為 void),因此它不會(huì)返回值,而這就是編譯器產(chǎn)生錯(cuò)誤的原因所在。為了處理此情況,Visual Basic 2010 引入了對(duì)語(yǔ)句 lambda 的支持,后者是包含一個(gè)或多個(gè)語(yǔ)句的 lambda:
Array.ForEach(customers, Sub(c) Console.WriteLine(c.Country))
由于 Console.WriteLine 不返回值,因此我們可以只創(chuàng)建 Sub lambda,而不是 Function lambda。下面是使用多個(gè)語(yǔ)句的另一個(gè)示例:
Array.ForEach(customers, Sub(c)
Console.WriteLine("Country Name:")
Console.WriteLine(c.Country)
End Sub)
當(dāng)此代碼運(yùn)行時(shí),它將為每個(gè)客戶(hù)打印兩行。另外請(qǐng)注意,如果在編碼時(shí)懸停在 c 上,您將看到編譯器會(huì)將類(lèi)型推斷為 Customer(鍵入 c As Customer 來(lái)顯式聲明類(lèi)型也是合法的)。動(dòng)態(tài)編寫(xiě)事件處理程序是語(yǔ)句 lambda 的另一個(gè)出色用途:
AddHandler b.Click, Sub(sender As Object, e As EventArgs)
MsgBox("Button Clicked")
'insert more complex logic here
End Sub
并且,事實(shí)上,您可以將語(yǔ)句 lambda 與 Visual Basic 2008 中引入的一項(xiàng)功能(松散委托)結(jié)合使用。(可以使用委托 – 類(lèi)型安全的函數(shù)指針 – 一次性執(zhí)行多個(gè)函數(shù)。)這種組合將生成更為簡(jiǎn)單的簽名:
AddHandler b.Click, Sub()
MsgBox("Button Clicked")
'insert more complex logic here
End Sub
委托松散使您可以完全忽略事件處理程序中的參數(shù) – 這是一個(gè)很好的優(yōu)點(diǎn),只要它們根本未使用過(guò),因此它們只會(huì)在視覺(jué)上帶來(lái)干擾。
除了到目前為止我們已看到的單行 Sub lambda 和多行 Sub lambda 外,Visual Basic 2010 還支持多行 Function lambda:
Dim query = customers.Where(Function(c)
'Return only customers that have not been saved
'insert more complex logic here
Return c.ID = -1
End Function)
語(yǔ)句 lambda 的另一個(gè)引人關(guān)注的方面是它們與 Visual Basic 2008 引入的匿名委托的相交方式。人們經(jīng)常將這些委托與 C# 的匿名方法混淆,盡管?chē)?yán)ge來(lái)說(shuō)它們并不相同。當(dāng) Visual Basic 編譯器基于 lambda 的方法簽名推斷委托類(lèi)型時(shí),將發(fā)生匿名委托:
Dim method = Function(product As String)
If product = "Paper" Then
Return 4.5 'units in stock
Else
Return 10 '10 of everything else
End If
End Function
MsgBox(method("Paper"))
如果運(yùn)行此代碼,您將看到消息框中顯示值 4.5。此外,如果懸停在 method 上,您將看到文本 Dim method As <Function(String) As Double>。由于我們未提供實(shí)際委托類(lèi)型,因此編譯器將自動(dòng)生成一個(gè)委托類(lèi)型,如下所示:
Delegate Function $compilerGeneratedName$(product As String) As Double
這稱(chēng)為匿名委托,因?yàn)樗粫?huì)出現(xiàn)在編譯器生成的代碼中,而不會(huì)出現(xiàn)在編寫(xiě)的代碼中。請(qǐng)注意,當(dāng)事實(shí)上沒(méi)有提供 As 子句來(lái)指定 lambda 的返回類(lèi)型時(shí),編譯器將返回類(lèi)型推斷為 Double。編譯器將查看 lambda 內(nèi)的所有返回語(yǔ)句,并將確定類(lèi)型 Double (4.5) 和 Integer (10):
'Notice the "As Single"
Dim method = Function(product As String) As Single
If product = "Paper" Then
Return 4.5 'units in stock
Else
Return 10 '10 of everything else
End If
End Function
然后,它將運(yùn)行其基準(zhǔn)類(lèi)型算法,并確定它能夠安全地將 10 轉(zhuǎn)換為 Double,但無(wú)法安全地將 4.5 轉(zhuǎn)換為 Integer;因此 Double 是更好的選擇。
您也可以顯式控制返回類(lèi)型,在這種情況下,編譯器將不會(huì)嘗試推斷類(lèi)型。非常常見(jiàn)的做法是將 lambda 賦給具有顯式委托類(lèi)型的變量,而不是依賴(lài)于編譯器來(lái)推斷委托類(lèi)型:
Dim method As Func(Of String, Single) =
Function(product)
If product = "Paper" Then
Return 4.5 'units in stock
Else
Return 10 '10 of everything else
End If
End Function
由于提供了顯式目標(biāo)類(lèi)型,因此無(wú)需聲明 As String 或 As Single;編譯器可基于語(yǔ)句左邊的委托類(lèi)型來(lái)推斷出其存在。因此,如果您懸停在 product 上,將會(huì)發(fā)現(xiàn)推斷出的類(lèi)型為 String。不再必須指定 As Single,因?yàn)槲蓄?lèi)型已提供該信息。在前面的示例中,F(xiàn)unc 委托(.NET Framework 包括該委托)的簽名如下所示:
Delegate Function Func(Of T, R)(ByVal param As T) As R
但有一個(gè)很小的例外之處,稍后我們將在“泛型方差”一節(jié)中看到。
自動(dòng)實(shí)現(xiàn)的屬性
在 Visual Basic 中,屬性是用于向外部公開(kāi)對(duì)象狀態(tài)的類(lèi)成員。典型的屬性聲明與如下聲明類(lèi)似:
Private _Country As String
Property Country As String
Get
Return _Country
End Get
Set(ByVal value As String)
_Country = value
End Set
End Property
一個(gè)實(shí)際上非常簡(jiǎn)單的概念就有 10 行代碼。由于典型的對(duì)象通常有數(shù)十個(gè)屬性,因此您最終會(huì)在類(lèi)定義中包括大量樣板代碼。為了簡(jiǎn)化此類(lèi)任務(wù),Visual Basic 2010 引入了自動(dòng)實(shí)現(xiàn)的屬性,利用該屬性,您只需使用一行代碼即可定義簡(jiǎn)單的屬性:
Property Country As String
在這種情況下,編譯器將繼續(xù)運(yùn)行并自動(dòng)生成 Getter、Setter 和支持字段。支持字段的名稱(chēng)是始終為前面帶有下劃線(xiàn)字符的屬性的名稱(chēng):此例中為 _Country。這種命名約定在將自動(dòng)實(shí)現(xiàn)的屬性更改為常規(guī)屬性的情況下確保了二進(jìn)制序列化兼容性。只要支持字段的名稱(chēng)相同,二進(jìn)制序列化就將繼續(xù)工作。
您可使用自動(dòng)實(shí)現(xiàn)的屬性執(zhí)行的其中一項(xiàng)出色的功能是:指定在構(gòu)造函數(shù)運(yùn)行時(shí)設(shè)置屬性默認(rèn)值的初始值設(shè)定項(xiàng)。舉例來(lái)說(shuō),一個(gè)帶有實(shí)體類(lèi)的常見(jiàn)方案將主鍵設(shè)置為類(lèi)似于 -1 的值,以指示其處于未保存的狀態(tài)。代碼將如下所示:
Property ID As Integer = -1
當(dāng)構(gòu)造函數(shù)運(yùn)行時(shí),支持字段 (_ID) 將自動(dòng)設(shè)置為值 -1。初始值設(shè)定項(xiàng)語(yǔ)法也適用于引用類(lèi)型:
Property OrderList As List(Of Order) = New List(Of Order)
由于無(wú)需輸入兩次類(lèi)型的名稱(chēng),因此上一行代碼可能不會(huì)具有非常明顯的“Visual Basic 特征”。好消息是,常規(guī)變量聲明中有一個(gè)與 Visual Basic 所允許語(yǔ)法一致的更短的語(yǔ)法:
Property OrderList As New List(Of Order)
您甚至能夠?qū)⒋苏Z(yǔ)法與對(duì)象初始值設(shè)定項(xiàng)結(jié)合使用,以允許設(shè)置其他屬性:
Property OrderList As New List(Of Order) With {.Capacity = 100}
很顯然,對(duì)于更復(fù)雜的屬性,擴(kuò)展的語(yǔ)法仍然是必要的。您仍然可以鍵入 Property{Tab} 來(lái)激活舊屬性片段?;蛘?,在鍵入屬性的第一行后,您可以只輸入 Get{Enter},IDE 將生成舊樣式的屬性:
Property Name As String
Get
End Get
Set(ByVal value As String)
End Set
End Property
人們通常會(huì)發(fā)現(xiàn):新的屬性語(yǔ)法與公共字段的語(yǔ)法幾乎相同,那么為什么不改為使用公共字段?有幾個(gè)原因:
大多數(shù) .NET 數(shù)據(jù)綁定基礎(chǔ)結(jié)構(gòu)都依據(jù)屬性(而不是字段)工作。
接口無(wú)法強(qiáng)制要求存在字段;但可以強(qiáng)制要求存在屬性。
屬性為更改業(yè)務(wù)規(guī)則提供了更長(zhǎng)期的靈活性。例如,假定某人引入了電話(huà)號(hào)碼必須為 10 位數(shù)的規(guī)則。如果分配給公共字段,將無(wú)法執(zhí)行此驗(yàn)證。對(duì)于諸如二進(jìn)制序列化和反射等方案而言,將公共字段更改為屬性是一項(xiàng)重大更改。
集合初始值設(shè)定項(xiàng)
一種常見(jiàn) .NET 做法是實(shí)例化集合,然后通過(guò)為每個(gè)元素調(diào)用一次 Add 方法來(lái)填充該集合:
Dim digits As New List(Of Integer)
digits.Add(0)
digits.Add(1)
digits.Add(2)
digits.Add(3)
digits.Add(4)
digits.Add(5)
digits.Add(6)
digits.Add(7)
digits.Add(8)
digits.Add(9)
但對(duì)于從根本上而言非常簡(jiǎn)單的概念來(lái)說(shuō),將會(huì)產(chǎn)生大量語(yǔ)法開(kāi)銷(xiāo)。Visual Basic 2010 引入了集合初始值設(shè)定項(xiàng),使您能夠更輕松地實(shí)例化集合。對(duì)于此代碼:
Dim digits = New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
編譯器將自動(dòng)生成對(duì) Add 方法的所有調(diào)用。您也可以使用 Visual Basic 的 As New 語(yǔ)法的功能:
Dim digits As New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
請(qǐng)注意,在 Visual Basic Team 上,我們一直建議使用第二種語(yǔ)法 (As New),而不是前者,因?yàn)樗勾a更能適應(yīng) Option Infer 設(shè)置的更改。
您可以依據(jù)滿(mǎn)足以下要求的任何類(lèi)型使用集合初始值設(shè)定項(xiàng):
您可以使用 For Each 語(yǔ)句循環(huán)訪問(wèn)該類(lèi)型 – 也就是說(shuō),該類(lèi)型實(shí)現(xiàn) IEnumerable。(有關(guān)集合類(lèi)型更精確/詳細(xì)的定義,請(qǐng)參見(jiàn) msdn.microsoft.com/library/aa711986(VS.71).aspx 上 Visual Basic 語(yǔ)言規(guī)范的第 10.9.3 節(jié))。
該類(lèi)型具有可訪問(wèn)的(但不一定是公共)無(wú)參數(shù)構(gòu)造函數(shù)。
該類(lèi)型具有可訪問(wèn)的(但不一定是公共)實(shí)例或名為 Add 的擴(kuò)展方法。
這意味著,您也可以將集合初始值設(shè)定項(xiàng)用于更復(fù)雜的類(lèi)型,例如字典:
Dim lookupTable As New Dictionary(Of Integer, String) From
{{1, "One"},
{2, "Two"},
{3, "Three"},
{4, "Four"}}
?。ㄕ?qǐng)注意,即使此語(yǔ)句跨了五行,也沒(méi)有下劃線(xiàn)字符。)在這種情況下,編譯器將生成與初始化字典的舊方法等效的代碼:
Dim lookupTable As New Dictionary(Of Integer, String)
lookupTable.Add(1, "One")
lookupTable.Add(2, "Two")
lookupTable.Add(3, "Three")
lookupTable.Add(4, "Four")
編譯器在調(diào)用具有兩個(gè) 參數(shù)(而不是一個(gè)參數(shù))的 Add 方法。它之所以知道這樣做,原因是傳入集合初始值設(shè)定項(xiàng)的值位于嵌套的大括號(hào)中,如下所示:{{1, “One”}, {2, “Two”}, …}。對(duì)于每一組嵌套的大括號(hào),編譯器會(huì)嘗試將這些參數(shù)傳遞到兼容的 Add 方法。
也可以通過(guò)使用擴(kuò)展方法來(lái)提供您自己的自定義 Add 實(shí)現(xiàn):
<Extension()>
Sub Add(ByVal source As IList(Of Customer),
ByVal id As Integer,
ByVal name As String,
ByVal city As String)
source.Add(New Customer With
{
.ID = id,
.Name = name,
.City = city
})
End Sub
?。纯此羞@些缺失的下劃線(xiàn)字符?。┐朔椒〝U(kuò)展任何實(shí)現(xiàn) IList(Of Customer) 的類(lèi)型,然后允許您使用新的集合初始值設(shè)定項(xiàng)語(yǔ)法,如下所示:
Dim list = New List(Of Customer) From
{
{1, "Jon", "Redmond"},
{2, "Bob", "Seattle"},
{3, "Sally", "Toronto"}
}
?。ㄏ蛄斜?中添加三個(gè)客戶(hù))。您也可以將集合初始值設(shè)定項(xiàng)與自動(dòng)實(shí)現(xiàn)的屬性結(jié)合使用:
Property States As New List(Of String) From {"AL", "AK", "AR", "AZ", ...}
數(shù)組文本
除了更強(qiáng)大的集合類(lèi)型處理方式外,Visual Basic 2010 還提供了一些用于處理數(shù)組的強(qiáng)大增強(qiáng)功能。假設(shè)有以下代碼(在較舊版本中可正常工作):
Dim numbers As Integer() = New Integer() {1, 2, 3, 4, 5}
通過(guò)查看該數(shù)組中的元素,很明顯每個(gè)元素都是整數(shù),因此,必須實(shí)際上在此行中打印輸出兩次 Integer 的操作不會(huì)真正添加任何值。數(shù)組文本 允許將某個(gè)數(shù)組的所有元素放在大括號(hào)內(nèi),然后讓編譯器自動(dòng)推斷類(lèi)型,從而創(chuàng)建該數(shù)組:
Dim numbers = {1, 2, 3, 4, 5}
numbers 的類(lèi)型不是 Object,而是 Integer()(只要啟用了“Option Infer”),原因是數(shù)組文本現(xiàn)在可代表本身,并且有其自己的類(lèi)型。假設(shè)有一個(gè)更復(fù)雜的示例:
Dim numbers = {1, 2, 3, 4, 5.555}
在這種情況下,numbers 的類(lèi)型將被推斷為 Double()。編譯器通過(guò)檢查數(shù)組中的每個(gè)元素并計(jì)算基準(zhǔn)類(lèi)型(所使用的算法與前面討論的用于推斷語(yǔ)句 lambda 的返回類(lèi)型的算法相同),從而確定類(lèi)型。如果沒(méi)有基準(zhǔn)類(lèi)型將會(huì)發(fā)生什么情況?例如,以下代碼中所示:
Dim numbers = {1, 2, 3, 4, "5"}
在這種情況下,將 Integer 轉(zhuǎn)換為 String 將會(huì)縮小轉(zhuǎn)換范圍(也就是說(shuō),在運(yùn)行時(shí)可能會(huì)出現(xiàn)數(shù)據(jù)丟失的情況),同樣,將 String 轉(zhuǎn)換為 Integer 也會(huì)縮小轉(zhuǎn)換范圍??蛇x擇的唯一安全的類(lèi)型為 Object()(如果啟用了“Option Strict”,編譯器將產(chǎn)生錯(cuò)誤)。
可以嵌套數(shù)組文本以形成多維數(shù)組或交錯(cuò)數(shù)組:
'2-dimensional array
Dim matrix = {{1, 0}, {0, 1}}
'jagged array - the parentheses force evaluation of the inner array first
Dim jagged = { ({1, 0}), ({0, 1}) }
動(dòng)態(tài)語(yǔ)言運(yùn)行時(shí)
盡管 Visual Basic 從技術(shù)上而言實(shí)質(zhì)上是靜態(tài)語(yǔ)言,但它一直有非常強(qiáng)大的動(dòng)態(tài)功能,例如晚期綁定。Visual Studio 2010 附帶了一個(gè)名為動(dòng)態(tài)語(yǔ)言運(yùn)行時(shí) (DLR) 的新平臺(tái),利用該平臺(tái)可更為輕松地生成動(dòng)態(tài)語(yǔ)言 – 并在這些語(yǔ)言之間通信。Visual Basic 2010 已更新為在其晚期綁定程序中完全支持 DLR,從而使開(kāi)發(fā)人員能夠使用采用其他語(yǔ)言(例如 IronPython/IronRuby)開(kāi)發(fā)的庫(kù)和框架。
此功能的一項(xiàng)突出優(yōu)點(diǎn)是,從語(yǔ)法上而言沒(méi)有任何內(nèi)容發(fā)生更改(事實(shí)上,在編譯器中沒(méi)有修改任何一行代碼來(lái)支持此功能)。開(kāi)發(fā)人員仍然能夠像在 Visual Basic 以前的版本中一樣進(jìn)行晚期綁定的操作。發(fā)生變化的是 Visual Basic 運(yùn)行庫(kù) (Microsoft.VisualBasic.dll) 中的代碼,該運(yùn)行庫(kù)現(xiàn)在可識(shí)別 DLR 提供的 IDynamicMetaObjectProvider 接口。如果某個(gè)對(duì)象實(shí)現(xiàn)此接口,則 Visual Basic 運(yùn)行庫(kù)將構(gòu)建 DLR CallSite,并允許該對(duì)象及提供該對(duì)象的語(yǔ)言將它們自己的語(yǔ)義注入操作。
例如,Python 標(biāo)準(zhǔn)庫(kù)包含一個(gè)名為 random.py 的文件,其中有一個(gè)名為 shuffle 的方法,該方法可用于隨機(jī)重新排列數(shù)組中的元素。調(diào)用該方法很簡(jiǎn)單:
Dim python As ScriptRuntime = Python.CreateRuntime()
Dim random As Object = python.UseFile("random.py")
Dim items = {1, 2, 3, 4, 5, 6, 7}
random.shuffle(items)
在運(yùn)行時(shí),Visual Basic 會(huì)看到對(duì)象實(shí)現(xiàn) IDynamicMetaObjectProvider,并因此將控制權(quán)交給 DLR,后者隨后將與 Python 通信,并執(zhí)行該方法(將 Visual Basic 中定義的數(shù)組作為參數(shù)傳遞給該方法)。
這是調(diào)用啟用了 DLR 的 API 的一個(gè)示例,但開(kāi)發(fā)人員也可以創(chuàng)建他們自己的使用此功能的 API。關(guān)鍵是實(shí)現(xiàn) IDynamicMetaObjectProvider 接口,在這種情況下,Visual Basic 和 C# 編譯器將可識(shí)別具有特殊動(dòng)態(tài)語(yǔ)義的對(duì)象。請(qǐng)不要手動(dòng)實(shí)現(xiàn)該接口,更簡(jiǎn)單的方法是:從 System.Dynamic.DynamicObject 類(lèi)(該類(lèi)已實(shí)現(xiàn)此接口)繼承并僅僅重寫(xiě)少數(shù)幾個(gè)方法。圖 3 顯示了創(chuàng)建自定義動(dòng)態(tài)對(duì)象(一種似乎可實(shí)時(shí)創(chuàng)建屬性的“屬性包”)并使用正常 Visual Basic 晚期綁定來(lái)調(diào)用該對(duì)象的完整示例。(有關(guān)使用 DynamicObject 的詳細(xì)信息,請(qǐng)閱讀 Doug Rothaus 撰寫(xiě)的非常不錯(cuò)的文章,網(wǎng)址為 blogs.msdn.com/vbteam/archive/2010/01/20/fun-with-dynamic-objects-doug-rothaus.aspx。)
圖 3 創(chuàng)建自定義動(dòng)態(tài)對(duì)象并使用 Visual Basic 晚期綁定調(diào)用該對(duì)象
Imports System.Dynamic
Module Module1
Sub Main()
Dim p As Object = New PropertyBag
p.One = 1
p.Two = 2
p.Three = 3
Console.WriteLine(p.One)
Console.WriteLine(p.Two)
Console.WriteLine(p.Three)
End Sub
Class PropertyBag : Inherits DynamicObject
Private values As New Dictionary(Of String, Integer)
Public Overrides Function TrySetMember(
ByVal binder As SetMemberBinder,
ByVal value As Object) As Boolean
values(binder.Name) = value
Return True
End Function
Public Overrides Function TryGetMember(
ByVal binder As GetMemberBinder,
ByRef result As Object) As Boolean
Return values.TryGetValue(binder.Name, result)
End Function
End Class
End Module
泛型方差
這是一項(xiàng)乍聽(tīng)起來(lái)的確可能很復(fù)雜的功能(帶有像協(xié)方差和逆變這樣的術(shù)語(yǔ)),但實(shí)際上它很簡(jiǎn)單。如果您有類(lèi)型為 IEnumerable(Of Apple) 的對(duì)象,并且希望將其分配給 IEnumerable(Of Fruit),這應(yīng)是合法的,因?yàn)槊總€(gè) Apple 都是 Fruit(由繼承關(guān)系強(qiáng)制要求)。遺憾的是,在 Visual Basic 2010 之前,編譯器中不支持泛型方差,即使公共語(yǔ)言運(yùn)行時(shí) (CLR) 中實(shí)際上支持泛型方差也是如此。
讓我們看一下圖 4 中的示例。在 Visual Basic 2008 中,圖 4 中的代碼將在 Dim enabledOnly 行上產(chǎn)生編譯錯(cuò)誤(或者,如果禁用了“Option Strict”,則產(chǎn)生運(yùn)行時(shí)異常)。解決方法是調(diào)用 .Cast 擴(kuò)展方法,如下所示:
'Old way, the call to Cast(Of Control) is no longer necessary in VB 2010
Dim enabledOnly = FilterEnabledOnly(buttons.Cast(Of Control))
這一點(diǎn)不再必要,因?yàn)樵?Visual Basic 2010 中,已通過(guò)使用 Out 修飾符將 IEnumerable 接口標(biāo)記為協(xié)變:
Interface IEnumerable(Of Out T)
...
End Interface
圖 4 泛型方差示例
Option Strict On
Public Class Form1
Sub Form1_Load() Handles MyBase.Load
Dim buttons As New List(Of Button) From
{
New Button With
{
.Name = "btnOk",
.Enabled = True
},
New Button With
{
.Name = "btnCancel",
.Enabled = False
}
}
Dim enabledOnly = FilterEnabledOnly(buttons)
End Sub
Function FilterEnabledOnly(
ByVal controls As IEnumerable(Of Control)
) As IEnumerable(Of Control)
Return From c In controls
Where c.Enabled = True
End Function
End Class
這意味著泛型參數(shù) T 現(xiàn)在為變量(也就是說(shuō),它適用于繼承關(guān)系),并且編譯器將確保僅在類(lèi)型來(lái)自于接口的位置使用該參數(shù)。泛型參數(shù)也可以是逆變量,這意味著它們僅在輸入 位置使用。類(lèi)型可實(shí)際上具有這兩者。例如,前面討論的 Func 委托既具有逆變參數(shù)(傳入的內(nèi)容),也具有協(xié)變參數(shù)(用于返回類(lèi)型):
Delegate Function Func(Of In T, Out R)(ByVal param As T) As R
可以在自定義接口和委托上使用 In 和 Out 修飾符。.NET Framework 4 中的許多常用接口和委托已標(biāo)記為變量;常見(jiàn)示例包括所有 Action/Func 委托、IEnumerable(Of T)、IComparer(Of T) 和 IQueryable(Of T) 等。
泛型方差的突出優(yōu)點(diǎn)是:它是一項(xiàng)您完全無(wú)需擔(dān)心的功能 – 如果它在執(zhí)行工作,您將絕不會(huì)注意到它。曾經(jīng)會(huì)導(dǎo)致編譯器錯(cuò)誤或要求調(diào)用 .Cast(Of T) 的情形在 Visual Basic 2010 中工作正常。
改進(jìn)的可選參數(shù)
可選參數(shù)提供了一種有用的高效功能,它使開(kāi)發(fā)人員能夠建立更靈活的方法,并避免使用許多方法重載使類(lèi)混亂不堪。在過(guò)去有一點(diǎn)限制,即可選參數(shù)不能為 null(或者甚至不能為任何非內(nèi)部結(jié)構(gòu)類(lèi)型)。Visual Basic 2010 現(xiàn)在允許您定義任意 值類(lèi)型的可選參數(shù):
Sub DisplayOrder(ByVal customer As Customer,
ByVal orderID As Integer,
Optional ByVal units As Integer? = 0,
Optional ByVal backgroundColor As Color = Nothing)
End Sub
在此例中,units 的類(lèi)型為 Nullable(Of Integer),backgroundColor 為非內(nèi)容結(jié)構(gòu)類(lèi)型,但仍然將它們用作可選參數(shù)。Visual Basic 2010 還對(duì)泛型可選參數(shù)提供了更好的支持。
嵌入式互操作類(lèi)型
對(duì)于執(zhí)行 COM 互操作的應(yīng)用程序,一個(gè)常見(jiàn)弱點(diǎn)是必須要使用主互操作程序集 (PIA)。PIA 是一種 .NET 程序集,它充當(dāng) COM 組件上的運(yùn)行時(shí)可調(diào)用包裝 (RCW),并具有用來(lái)標(biāo)識(shí)它的唯一 GUID 。.NET 程序集與 PIA 通信,后者隨后執(zhí)行任何必要的封送以在 COM 和 .NET 之間移動(dòng)數(shù)據(jù)。
遺憾的是,PIA 可能會(huì)使部署變得很復(fù)雜,因?yàn)樗鼈兪切枰渴鸬阶罱K用戶(hù)計(jì)算機(jī)的附加 DLL。它們還可能會(huì)導(dǎo)致版本控制問(wèn)題 – 例如,如果您希望應(yīng)用程序能夠同時(shí)依據(jù) Excel 2003 和 Excel 2007 工作,則將需要隨應(yīng)用程序一起同時(shí)部署兩個(gè) PIA。
嵌入式互操作類(lèi)型功能直接嵌入應(yīng)用程序,但只會(huì)嵌入絕對(duì)必要的 PIA 中的類(lèi)型和成員,因此無(wú)需將 PIA 部署到最終用戶(hù)的計(jì)算機(jī)。
若要為現(xiàn)有對(duì)象啟用此功能(對(duì)于新引用,默認(rèn)情況已啟用此功能),請(qǐng)?jiān)诮鉀Q方案資源管理器中選擇引用,并在屬性窗口中更改“Embed Interop Types”選項(xiàng)(請(qǐng)參見(jiàn)圖 5)?;蛘?,如果使用ming令行編譯器進(jìn)行編譯,請(qǐng)使用 /l(或 /link)開(kāi)關(guān),而不是 /r 和 /reference。

圖 5 在解決方案資源管理器中啟用嵌入式互操作類(lèi)型
啟用此功能之后,應(yīng)用程序?qū)⒉辉僖蕾?lài)于 PIA。事實(shí)上,如果在 Reflector 或 ildasm 中打開(kāi)程序集,您將注意到實(shí)際上根本沒(méi)有對(duì) PIA 的任何引用。
多重目標(biāo)
Visual Basic 2010 中所有功能的最突出特點(diǎn)是:您甚至可以在目標(biāo)為 .NET Framework 2.0 至 .NET Framework 3.5 的項(xiàng)目中使用這些功能。這意味著,隱式行繼續(xù)符、數(shù)組文本、集合初始值設(shè)定項(xiàng)、語(yǔ)句 lambda、自動(dòng)實(shí)現(xiàn)的屬性等功能將全部都可在現(xiàn)有項(xiàng)目中使用,而不必將目標(biāo)重定為 .NET Framework 4。
例外情況是嵌入式互操作類(lèi)型,它依賴(lài)于只有 .NET Framework 4 中才有的類(lèi)型;因此,如果將目標(biāo)定為 .NET Framework 版本 2.0 至 3.5,則無(wú)法使用該功能。此外,只會(huì)采用 .NET Framework 4 中的方式對(duì)標(biāo)記為變量的類(lèi)型進(jìn)行標(biāo)記,因此,在前面的示例中,如果將目標(biāo)定為版本 2.0 至 3.5,則仍然必須調(diào)用 .Cast(Of T)。不過(guò),如果將目標(biāo)定為這些早期版本,您可以建立自己的變量類(lèi)型(使用 In/Out 修飾符)。
若要更改應(yīng)用程序的當(dāng)前目標(biāo)框架,請(qǐng)雙擊“我的項(xiàng)目”,單擊“編譯”選項(xiàng)卡,單擊“高級(jí)編譯選項(xiàng)”,然后從底部的組合框中進(jìn)行選擇。
在從ming令行中進(jìn)行編譯時(shí),實(shí)際上沒(méi)有ming令行開(kāi)關(guān)可啟用此功能。實(shí)際上,編譯器將查看哪個(gè)程序集提供了 System.Object 的定義(通常為 mscorlib)以及程序集的目標(biāo)定為哪個(gè)框架,然后在輸出程序集中標(biāo)記該值。(編譯器在生成 Silverlight 程序集時(shí)也使用這個(gè)同樣的機(jī)制。)在使用 IDE 時(shí),所有這些都是以透明方式進(jìn)行的,因此通常您無(wú)需擔(dān)心任何事情。
歡迎試用
如您所見(jiàn),Visual Basic 2010 具有許多強(qiáng)大功能,這些功能使您能夠提高工作效率,同時(shí)減少編寫(xiě)的代碼行數(shù),而將更多工作交給編譯器來(lái)做。在本文中,我只探討了語(yǔ)言功能,但 Visual Basic 2010 IDE 還有數(shù)不勝數(shù)的出色增強(qiáng)功能。下面列出部分增強(qiáng)功能:
導(dǎo)航到
突出顯示引用
從使用中生成
更好的 IntelliSense(子字符串匹配、駝峰式大小寫(xiě)查找、建議模式 – 對(duì)于“首先測(cè)試”開(kāi)發(fā)風(fēng)ge非常有用)
多監(jiān)視器支持
縮放
Visual Basic 團(tuán)隊(duì)渴望聽(tīng)到您對(duì)我們改善 Visual Basic 的工作方面的反饋,因此請(qǐng)?jiān)?Microsoft Connect 上將您的意見(jiàn)和問(wèn)題發(fā)送給我們。若要了解有關(guān)語(yǔ)言和 IDE 功能的詳細(xì)信息,請(qǐng)查看 msdn.com/vbasic 上的內(nèi)容,其中包括文章、示例和操作方法視頻。當(dāng)然,最佳學(xué)習(xí)方式是深入研究和使用產(chǎn)品,因此是安裝和試用產(chǎn)品的時(shí)候了。
新聞熱點(diǎn)
疑難解答