程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> Visual Basic語言 >> VB.NET >> Visual Basic 9.0概述

Visual Basic 9.0概述

編輯:VB.NET

簡介

Visual Basic 一直以生成實用的、面向數據的業務線應用程序為中心。盡管遷移到 .NET 為應用程序開發人員帶來了統一的框架和托管平台,但是下一版本的 Visual Basic 包括一組對開發人員構建面向數據的應用程序時的工作效率影響深遠的功能。這些語言擴展引入了適用於所有數據源的通用查詢工具,不管是關系對象圖、分層對象圖還是 XML 文檔。

本文檔將簡略介紹這些新增功能。

Visual Basic 9.0 入門

要查看這些語言功能的實際作用,讓我們首先看一個真實的示例 - CIA World Factbook 數據庫。該數據庫包含有關世界各國的各種地理、經濟、社會和政治信息。為了演示示例,我們從每個國家/地區的名稱及其首都/首府、總面積和總人口的架構開始。通過使用以下類(為簡便起見,使用偽代碼),在 Visual Basic 9.0 中表示此架構:

Class Country
 Public Property Name As String
 Public Property Area As Long
 Public Property Population As Integer
End Class

下面是一個將用作運行示例的國家/地區數據庫的小子集:

Dim countries = {
 New Country With { .Name = "Palau", .Area = 458, .Population = 16952 }, _
 New Country With { .Name = "Monaco", .Area = 1.9, .Population = 31719 }, _
 New Country With { .Name = "Belize", .Area = 22960, .Population = 219296 }, _
 New Country With { .Name = "Madagascar", .Area = 587040, .Population =
13670507}}

有了此列表,可以使用以下查詢表達式來查詢其人口少於一百萬的所有國家/地區:

Dim smallCountries = From country In countries _
           Where country.Population < 1000000 _
Select country
For Each country In SmallCountries
 Console.WriteLine(country.Name)
Next

由於只有馬達加斯加的居住人口超過一百萬,因此在編譯和運行後,上述程序將打印出下列國家/地區名稱:

Palau
Monaco
Belize

讓我們檢查一下該程序,了解使得編寫此程序如此簡單的 Visual Basic 9.0 功能。首先,表示 Countries 的每個表達式的聲明都使用新增的“對象初始值設定項”語法 New Country With {..., .Area = 458, ...},通過類似於現在 With 語句的、基於表達式的簡明語法創建復雜的對象實例。

該聲明還說明了“隱式類型的局部變量”聲明,其中編譯器通過聲明右側的初始值設定項表達式來推斷局部變量 Countries 的類型。上述聲明完全等效於 Country() 類型的顯式類型局部變量聲明。

Dim countries As Country() = {...}

重申一下,這仍然是強類型聲明;編譯器已自動推斷局部聲明右側的類型,程序員無需在程序中手動輸入該類型。

使用 SQL 樣式的查詢表達式初始化局部變量聲明 SmallCountries,篩選出居住人口少於一百萬的所有國家/地區。查詢表達式與 SQL 類似是有意的,這樣已經了解 SQL 的程序員就可以更快地了解 Visual Basic 查詢語法。

Dim smallCountries = From country In Countries _
           Where country.Population < 1000000 _
  Select country

請注意,此代碼示例表示另一個隱式類型的應用程序:編譯器根據查詢表達式的結果類型,將 SmallCountries 的類型推斷為 IEnumerable(Of Country)。編譯器將查詢表達式本身轉換為對啟用 LINQ 的 API(它為實現 IEnumerable(Of T) 的所有類型實現查詢運算符)的調用。在這種情況下,轉換就像下面一樣簡單:

Dim smallCountries As IEnumerable(Of Country) = _
  Countries.Where(Function(country) country.Population < 1000000). _
       Select(Function(country) country)

擴展語法取決於“lambda 表達式”,後者表示返回表達式結果的內聯函數。lambda 表達式被轉換為委托並傳遞到擴展函數 Where,該函數在標准查詢運算符庫中被定義為 IEnumerable(Of T) 接口的擴展。

我們已了解 Visual Basic 9.0 的幾項新增功能,接下來讓我們深入了解更多內容。

隱式類型的局部變量

在隱式類型的局部變量聲明中,局部變量的類型是通過局部聲明語句右側的初始值設定項表達式推斷的。例如,編譯器推斷以下所有變量聲明的類型:

Dim population = 31719
Dim name = "Belize"
Dim area = 1.9
Dim country = New Country With { .Name = "Palau", ...}

因此,它們完全等效於以下顯式類型聲明:

Dim population As Integer = 31719
Dim name As String = "Belize"
Dim area As Float = 1.9
Dim country As Country = New Country With { .Name = "Palau", ...}

由於局部變量聲明的類型是通過新增的 Option Infer On(新項目的默認值)推斷的,因此不管 Option Strict 的設置如何,對此類變量的訪問始終是早期綁定的。程序員必須在 Visual Basic 9.0 中顯式指定後期綁定,方法是將變量顯式聲明為 Object 類型,如下所示:

Dim country As Object = New Country With { .Name = "Palau", ... }

推斷類型可防止意外使用後期綁定,更重要的是,它允許為新數據類型(如 XML)綁定強大擴展,如下所示。

For...Next 或 For Each...Next 語句中的循環控制變量也可以是隱式類型的變量。指定循環控制變量時(如 For I = 0 To SmallCountries.Count 或 For Each country In smallCountries 中所示),標識符定義一個新的隱式類型局部變量,其類型通過初始值設定項或集合表達式推斷且作用於整個循環。通過應用此類型推斷,可以重新編寫打印所有小國家/地區的循環,如下所示:

For Each country In smallCountries
 Console.WriteLine(country.Name)
Next

country 的類型被推斷為 Country,即 SmallCountries 的元素類型。

對象和數組初始值設定項

在 Visual Basic 中,With 語句簡化了對一個聚合值的多個成員的訪問,而無須多次指定目標表達式。在 With 語句塊內,對以句點開頭的成員訪問表達式進行計算,好像句點前面有 With 語句的目標表達式。例如,以下語句初始化新的 Country 實例,隨後將其字段初始化為所需的值:

Dim palau As New Country()
With palau
 .Name = "Palau" 
 .Area = 458
 .Population = 16952
End With

Visual Basic 9.0 中新增的“對象初始值設定項”是基於表達式的 With 語句,用於簡明地創建復雜的對象實例。使用對象初始值設定項,可以將上述兩條語句捕獲到單個(隱式類型的)局部聲明中,如下所示:

Dim palau = New Country With { _
 .Name = "Palau", _
 .Area = 458, _
 .Population = 16952 _
}

表達式中此樣式的對象初始化對查詢是很重要的。通常,查詢類似於由等號右側的 Select 子句初始化的對象聲明。由於 Select 子句返回表達式,因此必須能夠用單個表達式初始化整個對象。

正如我們所看到的那樣,對象初始值設定項還便於為復雜對象創建集合。通過使用“數組初始值設定項”表達式,可以初始化數組和推斷元素類型。例如,假定將城市的聲明作為類,

Class City
 Public Property Name As String
 Public Property Country As String
 Public Property Longitude As Long
 Public Property Latitude As Long
End Class

則可以為示例國家/地區創建首都/首府數組,如下所示:

Dim Capitals = { _
 New City With { _
  .Name = "Antanarivo", _
  .Country = "Madagascar", _
  .Longitude = 47.4, _
  .Latitude = -18.6 }, _
 New City With { _
  .Name = "Belmopan", _
  .Country = "Belize", _
  .Longitude = -88.5, _
  .Latitude = 17.1 }, _
 New City With { _
  .Name = "Monaco", _
  .Country = "Monaco", _
  .Longitude = 7.2, _
  .Latitude = 43.7 }, _
 New City With { _
  .Country = "Palau",
  .Name = "Koror", _
  .Longitude = 135, _
  .Latitude = 8 } _
}

匿名類型

通常,我們希望刪除或剔除一個類型的某些成員,從而得到查詢結果。例如,我們可能只希望知道所有位於熱帶的首都/首府的 Name 和 Country,那麼就使用源數據中的 Latitude 和 Longitude 列標識熱帶,而在結果中剔除其他列。在 Visual Basic 9.0 中,我們通過為其緯度位於北回歸線和南回歸線之間的每個城市 C 創建新的對象實例(而無須指定類型)來做到這一點:

Const TropicOfCancer = 23.5
Const TropicOfCapricorn = -23.5
Dim tropical = From city In Capitals _
        Where TropicOfCancer <= city.Latitude _
          AndAlso city.Latitude >= TropicOfCapricorn _
        Select New With {city.Name, city.Country}

局部變量 Tropical 的推斷類型是匿名類型實例的集合,即(使用偽代碼)IEnumerable(Of { Name As String, Country As String })。Visual Basic 編譯器將創建隱式類,例如 _Name_As_String_Country_As_String_(其成員名稱和類型是通過對象初始值設定項推斷的),如下所示:

Class _Name_As_String_Country_As_String_
  Public Property Name As String
  Public Property Country As String
  ...
End Class

在同一程序內,編譯器將合並相同的匿名類型。按相同順序指定相同名稱和類型的屬性序列的兩個匿名對象初始值設定項,將生成相同匿名類型的實例。在外部,Visual Basic 生成的匿名類型被替換為 Object,這樣編譯器就可以將匿名類型作為函數參數和結果統一進行傳遞。

由於匿名類型通常用於現有類型的項目成員,因此 Visual Basic 9.0 允許使用簡略計劃表示法 New With { city.Name, city.Country } 縮寫長格式的 New With { .Name = city.Name, .Country = city.Country }。如果在查詢理解的結果表達式中使用,則可以進一步縮寫計劃初始值設定項,如下所示:

Dim Tropical = From city In Capitals _
        Where TropicOfCancer <= city.Latitude _
          AndAlso city.Latitude >= TropicOfCapricorn _
        Select city.Name, city.Country

請注意,這兩種縮寫格式在意義上與上述長格式完全相同。

深入的 XML 支持

“LINQ to XML”是新增的內存中 XML 編程 API,專為利用最新的 .NET Framework 功能(如語言集成查詢框架)而設計。正如查詢理解通過基礎的標准 .NET Framework 查詢運算符添加熟悉、便利的語法一樣,Visual Basic 9.0 通過 “XML 文字”和“XML 屬性”提供對 LINQ to XML 的深入支持。

為了說明 XML 文字,讓我們通過實際上為平面關系數據源的 Countries 和 Capitals 進行查詢,從而構造分層 XML 模型,該模型將嵌套每個國家/地區的首都/首府(作為子元素)並計算人口密度(作為屬性)。

為找出給定國家/地區的首都/首府,我們對每個國家/地區的名稱成員與每個城市的國家/地區成員執行“聯接”操作。如果給定了某個國家/地區及其首都/首府,則可以通過將計算值填入嵌入式表達式“空洞”輕松構造 XML 片段。我們將使用與 ASP 類似的語法為 Visual Basic 表達式編寫“空洞”,如 Name=<%= country.Name %> 或 <Name><%= city.Name %></Name> 中所示。下面是將 XML 文字和查詢理解組合在一起的查詢:

Dim countriesWithCapital As XElement = _
  <Countries>
  <%= From country In Countries, city In Capitals _
    Where country.Name = city.Country _
    Select <Country Name=<%= country.Name %>
            Density=<%= country.Population / country.Area %>>
         <Capital>
          <Name><%= city.Name %></Name>
          <Longitude><%= city.Longitude %></Longitude>
          <Latitude><%= city.Latitude %></Latitude>
         </Capital>
        </Country> _
  %>
  </Countries>

請注意,可以從聲明中省略類型 XElement,在這種情況下將對它進行推斷,就像任何其他局部聲明一樣。

在此聲明中,要在 <Countries> 元素替換 Select 查詢的結果。 因此,Select 查詢是第一個“空洞”的內容,由 <Countries> 內常見的 ASP 樣式標記 <%= 和 %> 進行界定。由於 Select 查詢的結果是一個表達式,而且 XML 文字是表達式,因此在 Select 自身中嵌套另一個 XML 文字是很自然的。此嵌套文字本身包含 Country.Name 的嵌套屬性“空洞”和計算的人口密度比率 Country.Population/Country.Area,以及首都/首府名稱和坐標的嵌套元素“空洞”。

在編譯和運行後,上述查詢將返回以下 XML 文檔(為節省篇幅,對標准 IDE 輸出的格式稍作調整)

<Countries>
<Country Name="Palau" Density="0.037117903930131008">
  <Capital>
   <Name>Koror</Name><Longitude>135</Longitude><Latitude>8</Latitude>
  </Capital>
</Country>
<Country Name="Monaco" Density="16694.21052631579">
  <Capital>
   <Name>Monaco</Name><Longitude>7.2</Longitude><Latitude>3.7</Latitude>
  </Capital>
</Country>
<Country Name="Belize" Density="9.5512195121951216">
  <Capital>
   <Name>Belmopan</Name><Longitude>-88.5</Longitude><Latitude>17.1</Latitude>
  </Capital>
</Country>
<Country Name="Madagascar" Density="23.287181452711909">
  <Capital>
   <Name>Antananarivo</Name>
   <Longitude>47.4</Longitude><Latitude>-18.6</Latitude>
  </Capital>
 </Country>
</Countries>

Visual Basic 9.0 將 XML 文字編譯為常規 System.Xml.Linq 對象,從而確保 Visual Basic 與使用 LINQ to XML 的其他語言之間的完全互操作性。在我們的示例查詢中,編譯器生成的代碼(如果可以看到它)所示如下:

Dim countriesWithCapital As XElement = _
 New XElement("Countries", _
    From country In Countries, city In Capitals _
    Where country.Name = city.Country _
 Select New XElement("Country", _
       New XAttribute("Name", country.Name), _
       New XAttribute("Density", country.Population/country.Area), _
       New XElement("Capital", _
        New XElement("Name", city.Name), _
        New XElement("Longitude", city.Longitude), _
        New XElement("Latitude", city.Latitude))))

除構造 XML 外,Visual Basic 9.0 還通過 XML 屬性簡化了對 XML 結構的訪問;即,在運行時 Visual Basic 代碼中的標識符將被綁定到對應的 XML 屬性和元素。例如,可以打印所有示例國家/地區的人口密度,如下所示:

使用“子軸”countriesWithCapital.<Country> 獲取 countriesWithCapital XML 結構中的所有“Country”元素。

使用“屬性軸”country.@Density 獲取 Country 元素的“Density”屬性。

使用“子代軸”country...<Latitude>(在源代碼中為三個點)獲取 Country 元素的所有“Latitude”子元素,而不管它們在層次結構中的深度如何。

使用 IEnumerable(Of XElement) 上的“擴展屬性”.Value 選擇生成序列的第一個元素的值,或者使用擴展索引器 (i) 選擇第 i 個元素。

組合使用所有這些功能,可以大幅度地壓縮和簡化代碼:

For Each country In countriesWithCapital.<Country>
 Console.WriteLine("Density = " & country.@Density)
 Console.WriteLine("Latitude = " & country...<Latitude>.Value)
Next

當聲明、賦值或初始化的目標表達式為 Object 類型而不是某些更具體的類型時,編譯器知道針對常規對象使用後期綁定。同樣,當目標表達式屬於 XElement、XDocument 或 XAttribute 類型或集合時,編譯器知道針對 XML 使用綁定。

針對 XML 使用後期綁定後,編譯器將進行如下轉換:

子軸表達式 countriesWithCapital.<Country> 轉換為原始的 LINQ to XML 調用 countriesWithCapital.Elements("Country")(它返回 Country 元素中名為“Country”的所有子元素的集合)。

屬性軸表達式 country.@Density 轉換為 Country.Attribute("Density").Value(它返回 Country 中名為“Density”的單個子屬性)。

子代軸表達式 country...<Latitude> 轉換為原始的 LINQ to XML 調用 country.Descendants(“Latitude”)(它返回在 country 下任何深度上命名的所有元素的集合)。

查詢理解

“查詢運算符”是可以一次應用於整個集合中一組值的運算符,如 Select、Order By 或 Where。“查詢表達式”是將一系列查詢運算符應用於特定集合的表達式。例如,以下查詢表達式搜索一個國家/地區集合,返回居住人口少於一百萬的所有國家/地區的名稱:

Dim smallCountries = From country In Countries _
           Where country.Population < 1000000 _
           Select country

查詢表達式語法設計得與標准關系 SQL 語法非常接近,旨在使熟悉 SQL 的任何人無需接受多少指導就能夠使用查詢表達式。但是,該語法不受 SQL 的約束,查詢表達式也不用於將 SQL 轉換為 Visual Basic。由於 SQL 是根據純關系模型設計的,因此它的一些語言規則不能很好地在允許(甚至包含)層次結構的類型系統上運行。此外,一些 SQL 語法和語義元素與現有的 Visual Basic 語法或語義相沖突或者配合不好。因此,雖然熟悉 SQL 的人都應熟悉查詢表達式,但是仍存在需要了解的差異。

查詢表達式被轉換為對基礎序列運算符(位於 From 子句中被指定為源類型的可查詢類型上)的調用。如果序列運算符通常被定義為源類型上的“擴展方法”,則將它們綁定到處於作用域中的任何序列運算符。這意味著,通過導入特定的實現,可以將查詢表達式語法再次綁定到啟用 LINQ 的不同 API。這就是將查詢表達式再次綁定到使用 LINQ to SQL 或 LINQ to objects(在內存中執行查詢的局部查詢執行引擎)的實現的方法。

有些查詢運算符(如 From、Select 和 Group By)引入了名為“范圍變量”的特殊類型的局部變量。默認情況下,范圍變量的作用范圍是,從引入運算符到隱藏范圍變量並表示查詢計算時集合中單個行的屬性或列的運算符。例如,在以下查詢中:

Dim smallCountries = From country In Countries _
           Where country.Population < 1000000 _
           Select country

有些查詢運算符(如 Distinct)不使用或更改控制變量。其他查詢運算符(如 Select)隱藏作用域中的當前范圍變量,並引入新的范圍變量。例如,在以下查詢中:

Dim smallCountries = From country In Countries _
           Select country.Name, Pop = country.Population
           Order By Pop

Order By 查詢運算符只能訪問由 Select 運算符引入的 Name 和 Pop 范圍變量;如果 Order By 運算符嘗試引用 Country,則會出現編譯時錯誤。

如果查詢不以 Select 運算符結尾,則該集合得到的元素類型就像在查詢范圍內存在一個包含所有控制變量的 Select 計劃一樣:

Dim countriesWithCapital = From country In Countries, city In Capitals _
              Where country.Name = city.Country
The inferred type for this local declaration is (in pseudo-code to represent an anonymous type) IEnumerable(Of { Country As Country, City As City }).
查詢表達式與 SQL 的不同之處:組合性和分層數據

Visual Basic 9.0 中的“查詢表達式”是完全“組合的”,這意味著通過追加具有其他查詢運算符的查詢可以隨意嵌入或構造查詢理解。由於其組合性,只需分別了解各個單獨的子表達式即可輕松了解大型查詢,而且可以輕松地跟蹤流過每個查詢運算符的語義和類型。但是,使用以組合性作為設計原則的表達式編寫查詢的體驗與使用 SQL(將查詢分析為單個塊)有很大不同。

此外,啟用 LINQ 的 API 用於實現具有“延遲執行”的序列運算符。延遲執行意味著在枚舉結果之前不計算查詢。對於 LINQ to SQL,這意味著在請求結果之前不會將查詢遠程傳輸到 SQL。這意味著,將查詢分離到多條語句中並不表示會多次找到數據庫。因此,在 SQL 中通常是嵌套查詢,而在 LINQ 中就變成了組合查詢。

SQL 缺少組合性的一個原因是,基礎關系數據模型本身不是組合的。例如,表不能包含子表;換句話說,所有表都必須是平面的。因此,SQL 程序員編寫其結果為平面表、適合於 SQL 數據模型的單一表達式,而不是將復雜表達式分解為更小的單元。由於 Visual Basic 基於 CLR 類型系統,因此沒有限制哪些類型可以作為其他類型的組件出現。除了靜態類型規則外,對可以作為其他表達式的組件出現的表達式類型沒有限制。因此,不僅行、對象和 XML,而且 Active Directory、文件、注冊表項等在查詢源和查詢結果中都是一流成員。

查詢運算符

熟悉 SQL 實現的那些人將意識到,在基礎 .NET 序列運算符中,許多組合性關系代數運算符(如計劃、選擇、交叉積、分組和排序)都表示查詢處理器中的查詢計劃。

• •

Select 運算符指定輸出集合的形狀。

Where 和 Distinct 運算符限制集合的值。

Order By 運算符要求對集合排序。

Skip、Skip While、Take 和 Take While 運算符根據順序或條件返回集合的子集。

Union、Union All、Except 和 Intersect 運算符對兩個集合進行操作,然後生成一個集合。

Group By 運算符根據一個或多個關鍵字對集合進行分組。

Avg、Sum、Count、Min 和 Max 運算符聚合集合並生成值。

Any 和 All 運算符根據條件聚合集合並返回布爾值。

Join 運算符對兩個集合進行操作,並根據從元素派生的匹配項生成一個集合。

Group Join 運算符根據從元素提取的匹配項將兩個集合聯接在一起。

可以在完整的語言規范中找到所有運算符的完整語法。但是,為了便於說明,下面將查找每個國家/地區的首都/首府,並按首都/首府所在緯度對國家/地區名稱進行排序:

Dim countriesWithCapital = _
 From country In Countries _
 Join city In Capitals On country.Name Equals city.Country _
 Order By city.Latitude _
 Select country.Name

對於根據集合計算標量值的查詢,Aggregate 運算符將遍歷集合。以下查詢在一條語句中查找小國家/地區的數量並計算其平均人口密度:

Dim popInfo = _
 Aggregate country In Countries _
 Where country.Population < 1000000 _
 Into Total = Count(), Density = Average(country.Population/country.Area)

聚合函數最常用於組合分區的源集合。例如,可以根據是否位於熱帶對所有國家/地區進行分組,然後聚合每個組的計數。為此,可以將聚合運算符與 Group By 和 Group Join 子句聯合使用。在下面的示例中,Helper 函數 IsTropical 將封裝測試 City 是否為熱帶氣候:

 Function IsTropical() As Boolean
  Return TropicOfCancer =< Me.Latitude AndAlso Me.Latitude >= TropicOfCapricorn
 End Function

考慮到此 Helper 函數,使用與上面完全相同的聚合,但是首先將 Country 和 Capital 對的輸入集合分區到其 Country.IsTropical 相同的組中。在這種情況下,有兩個這樣的組:一個組包含熱帶國家/地區帕勞、伯利茲和馬達加斯加;另一個組包含非熱帶國家/地區摩納哥。

關鍵字 國家/地區 城市

Country.IsTropical() = True

帕勞

伯利茲

馬達加斯加

科羅爾

貝爾莫潘

塔那那利佛

Country.IsTropical() = False

摩納哥

摩納哥

然後,通過計算總數和平均密度,聚合這些組中的值。現在,結果類型是 Total As Integer 和 Density As Double 對的集合:

Dim countriesByClimate = _
 From country In Countries _
Join city In Capitals On country.Name Equals city.Country _
Group By country.IsTropical()
Into Total = Count(), Density = Average(country.Population/country.Area)

上述查詢未充分體現其復雜性。下面的查詢通過“lambda 表達式”和“擴展方法”用“方法調用語法”表示查詢可以獲得相同的結果。

Dim countriesByClimate7 = _
 countries. _
  SelectMany( _
   Function(country) Capitals, _
   Function(country, city) New With {country, city}). _
  Where(Function(it) it.country.Name = it.city.Country). _
  GroupBy( _
   Function(it) it.city.IsTropical(), _
   Function(IsTropical, Group) _
    New With { _
     IsTropical, _
     .Total = Group.Count(), _
     .Density = Group.Average( _
       Function(it) it.country.Population / it.country.Area _
     ) _
    } _
  )
擴展方法和 Lambda 表達式

.NET Framework 標准查詢基礎結構中的許多基礎功能來自“擴展方法”和“lambda 表達式”。擴展方法是標記有自定義屬性(允許通過實例方法語法調用它們)的共享方法。大多數的擴展方法具有類似的簽名。第一個參數是對應用方法的實例,第二個參數是要應用的謂詞。例如,從 Where 子句轉換來的 Where 方法具有以下簽名:

Module IEnumerableExtensions
 <Extension> _
 Function Where (Of TSource) _
  (Source As IEnumerable(Of TSource), _
   predicate As Func(Of TSource, Boolean)) As IEnumerable(Of TSource)
  ...
 End Function
End Module

由於許多標准查詢運算符(如 Where、Select、SelectMany 等)被定義為將 Func(Of S,T) 類型的委托視為參數的擴展方法,因此編譯器不再要求生成表示謂詞的委托。編譯器創建“結束對象”(捕獲其周圍上下文的委托),並將它們傳遞到基礎方法調用。例如,通過以下查詢,編譯器生成表示要傳遞到 Select 和 Where 函數的委托的 lambda 表達式:

Dim smallCountries = From country In countries _
           Where country.Population < 1000000 _
           Select country.Name

編譯器生成分別用於計劃和謂詞的兩個 lambda 表達式:

Function(Country As Country) country.Name
Function (Country As Country) country.Population < 1000000

上述查詢被轉換為對標准查詢框架的方法調用,將要應用的源和 lambda 表達式作為參數傳遞:

Dim smallCountries = _
 Enumerable.Select( _
   Enumerable.Where(countries, _
    Function (country As Country) country.Population < 1000000), _
    Function(country As Country) country.Name)

可為空的類型

關系數據庫為通常與普通編程語言不一致而且程序員通常不熟悉的可為空的值提供語義。在數據密集型應用程序中,程序明確無誤地處理這些語義是很重要的。意識到此必要性,在 .NET Frameworks 2.0 中 CLR 使用泛型類型 Nullable(Of T As Structure) 添加了對可為空的運行時支持。使用此類型,可以聲明值類型(如 Integer、Boolean、Date 等)的為空版本。由於顯而易見的原因,為空類型的 Visual Basic 語法為 T?。

例如,由於並非所有國家/地區都是獨立的,因此可以將新成員添加到表示其獨立日期的類 Country 中(如果適用):

Partial Class Country
 Public Property Independence As Date?
End Class

帕勞的獨立日期為 #10/1/1994#,而英屬維爾京群島是屬於英國的領土,因此其獨立日期為 Nothing。

Dim palau = _
 New Country With { _
  .Name = "Palau", _
  .Area = 458, _
  .Population = 16952, _
  .Independence = #10/1/1994# }
Dim virginIslands = _
 New Country With { _
  .Name = "Virgin Islands", _
  .Area = 150, _
  .Population = 13195, _
  .Independence = Nothing }

Visual Basic 9.0 將支持為空值進行三值邏輯和 Null 傳播算術,這意味著如果算術、比較、邏輯或按位、移位、字符串或類型運算的操作數之一為 Nothing,則結果將為 Nothing。如果這兩個操作數都有適當的值,則對操作數的基礎值執行運算,並將結果轉換成為空值。

由於 Palau.Independence 和 VirginIslands.Independence 都具有類型 Date?,因此編譯器將對下面的減法使用空傳播算術,這樣局部聲明 PLength 和 VILength 的推斷類型都將為 TimeSpan?。

Dim pLength = #8/24/2005# - Palau.Independence     ‘ 3980.00:00:00

由於兩個操作數都不為 Nothing,因此 PLength 的值是 3980.00:00:00。另一方面,由於 VirginIslands.Independence 的值是 Nothing,因此結果同樣屬於 TimeSpan? 類型,而由於空傳播,VILength 的值將是 Nothing。

Dim vILength = #8/24/2005# - virginIslands.Independence ‘ Nothing

與 SQL 一樣,比較運算符將執行空傳播,邏輯運算符將使用三值邏輯。在 If 和 While 語句中,Nothing 被解釋為 False;因此在下面的代碼片段中,將執行 Else 分支:

If vILength < TimeSpan.FromDays(10000)
 ...
Else
 ...
End If

請注意,在三值邏輯中,等同性檢查 X = Nothing,Nothing = X 的計算結果始終為 Nothing;為了檢查 X 是否為 Nothing,應使用兩值邏輯比較 X Is Nothing 或 Nothing Is X。

寬松委托

在 Visual Basic 8.0 中使用 AddressOf 或 Handles 創建委托時,專用於綁定到委托標識符的方法之一必須與委托類型的簽名完全匹配。在下面的示例中,OnClick 子例程的簽名必須與 Button 類型中在後台聲明的事件處理程序委托 Delegate Sub EventHandler(sender As Object, e As EventArgs) 的簽名完全匹配:

Dim WithEvents btn As New Button()
Sub OnClick(sender As Object, e As EventArgs) Handles B.Click
 MessageBox.Show("Hello World from" & btn.Text)
End Sub

但是,調用“非委托”函數和子例程時,Visual Basic 不要求實參與所嘗試調用的方法之一完全匹配。如以下片段所示,實際上可以使用 Button 類型和 MouseEventArgs 類型(它們分別是形參 Object 和 EventArgs 的子類型)的實參調用 OnClick 子例程。

Dim m As New MouseEventArgs(MouseButtons.Left, 2, 47, 11,0)
OnClick(btn, m)

相反,假定可以定義一個使用兩個 Object 參數的子例程 RelaxedOnClick,則可以用 Object 和 EventArgs 類型的實參調用它:

Sub RelaxedOnClick(sender As Object, e As Object) Handles btn.Click
 MessageBox.Show("Hello World from" & btn.Text))
End Sub
Dim e As EventArgs = m
Dim s As Object = btn
RelaxedOnClick(btn,e)

在 Visual Basic 9.0 中,對綁定到委托進行了放寬,以便與方法調用一致。即,如果可以用與形參完全匹配的實參“調用”函數或子例程並返回委托的類型,則可以將該函數或子例程綁定到委托。換句話說,委托的綁定和定義將遵循與方法調用相同的重載決策邏輯。

這意味著,在 Visual Basic 9.0 中現在可以將使用兩個 Object 參數的子例程 RelaxedOnClick 綁定到 Button 的 Click 事件:

Sub RelaxedOnClick(sender As Object, e As Object) Handles btn.Click
 MessageBox.Show(("Hello World from" & btn.Text)
End Sub

事件處理程序的兩個參數 sender 和 EventArgs 幾乎是無關緊要的。相反,處理程序會訪問在其上直接注冊事件的控件的狀態,並忽略它的兩個參數。為支持此常見情況,在不產生歧義的前提下,可以允許委托不帶任何參數。換句話說,可以只需編寫以下內容:

Sub RelaxedOnClick Handles btn.Click
 MessageBox.Show("Hello World from" & btn.Text)
End Sub

可以這樣理解,寬松委托在使用 AddressOf 或委托創建表達式構造委托時也適用,即使方法組為後期綁定調用:

Dim F As EventHandler = AddressOf RelaxedOnClick
Dim G As New EventHandler(AddressOf btn.Click)
結論

Visual Basic 9.0 統一了對數據的訪問,不管數據源自關系數據庫、XML 文檔還是任意對象圖,也不管以什麼方式保持或者存儲在內存中。該統一包括樣式、方法、工具和編程模式。Visual Basic 的語法極其靈活,可以輕松地將類似 XML 文字的擴展和類似 SQL 的查詢表達式添加到該語言深處。這大大減少了新的 .NET 語言集成查詢 API 的“外圍”,通過 IntelliSense 和智能標記提高了數據訪問功能的可發現性,並通過將外部語法從字符串數據中提取到 Visual Basic 中從而大大改進了調試和編譯時檢查功能。

此外,諸如類型推斷、對象初始值設定項和寬松委托之類的功能大大減少了代碼冗余以及程序員需要學習和記憶或查找的規則異常數目,而且不會對性能產生任何影響。

雖然可能看起來 Visual Basic 9.0 中的新增功能很多,但是我們希望上述主題將使您確信,它是一致的、及時的並致力於使 Visual Basic 成為世界上最好的編程語言。我們希望它也能激發您的創造力,並希望您也相信使用 Visual Basic 將有更加美妙的體驗。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved