匿名類型 (Visual Basic)
Visual Basic 支援匿名型別,其可讓您在建立物件時,不需要撰寫資料類型的類別定義。 編譯器 (Compiler) 會自動幫您建立類別 (Class)。 該類別沒有可使用的名稱,直接繼承自 Object,但是包含您在宣告物件時指定的屬性。 因為未指定資料類型的名稱,所以稱為「匿名型別」。
下列範例會宣告和建立變數 product
作為匿名型別執行個體,其具有下列兩個屬性:Name
和 Price
。
' Variable product is an instance of a simple anonymous type.
Dim product = New With {Key .Name = "paperclips", .Price = 1.29}
「查詢運算式」會使用匿名型別來合併查詢所選取的資料行。 因為您無法預測特定查詢可能選取的資料行,所以無法事先定義結果的類型。 匿名型別可讓您撰寫查詢,以任意順序選取任意數目的資料行。 編譯器會建立符合指定屬性和指定順序的資料類型。
在下列範例中,products
是產品物件的清單,每個物件都有許多屬性。 變數 namePriceQuery
保有查詢的定義,當查詢執行時,會傳回匿名型別執行個體的集合,其具有下列兩個屬性:Name
和 Price
。
Dim namePriceQuery = From prod In products
Select prod.Name, prod.Price
變數 nameQuantityQuery
保有查詢的定義,當其執行時,會傳回匿名型別執行個體的集合,其具有下列兩個屬性:Name
和 OnHand
。
Dim nameQuantityQuery = From prod In products
Select prod.Name, prod.OnHand
如需編譯器針對匿名型別所建立程式碼的詳細資訊,請參閱匿名型別定義。
警告
匿名型別的名稱是由編譯器所產生,且每次編譯都不盡相同。 因為名稱可能會隨著重新編譯專案時變更,所以您的程式碼不應該使用或依賴匿名型別的名稱。
宣告匿名型別
宣告匿名型別執行個體時,會使用初始設定式清單來指定類型的屬性。 您在宣告匿名型別時僅能指定屬性,而無法指定其他類別元素,例如方法或事件。 在下列範例中,product1
是匿名型別執行個體,具有下列兩個屬性:Name
和 Price
。
' Variable product1 is an instance of a simple anonymous type.
Dim product1 = New With {.Name = "paperclips", .Price = 1.29}
' -or-
' product2 is an instance of an anonymous type with key properties.
Dim product2 = New With {Key .Name = "paperclips", Key .Price = 1.29}
如果您將屬性指定為索引鍵屬性,就能用以比較兩個匿名型別執行個體是否相等。 不過,您無法變更索引鍵屬性的值。 如需詳細資訊,請參閱本主題稍後的<索引鍵屬性>一節。
請注意,宣告匿名型別的執行個體,就如同使用物件初始設定式宣告具名類型的執行個體:
' Variable product3 is an instance of a class named Product.
Dim product3 = New Product With {.Name = "paperclips", .Price = 1.29}
如需指定匿名型別屬性的其他方式詳細資訊,請參閱操作說明:推斷匿名型別宣告中的屬性名稱和類型。
索引鍵內容
索引鍵屬性與非索引鍵屬性具有下列數種基本上的差異:
僅會比較索引鍵屬性的值,以判斷兩個執行個體是否相等。
索引鍵屬性的值為唯讀,且無法變更。
匿名型別的編譯器所產生雜湊碼演算法中,只會包含索引鍵屬性值。
Equality
只有在匿名型別的執行個體是相同匿名型別的執行個體時,它們才能相等。 如果兩個執行個體符合下列條件,則編譯器會將兩個執行個體視為相同類型的執行個體:
在相同的組件中宣告。
其屬性具有相同名稱、相同的推斷型別,並以相同順序宣告。 名稱比較不區分大小寫。
每個執行個體中的相同屬性都標示為索引鍵屬性。
每個宣告中至少有一個屬性是索引鍵屬性。
沒有索引鍵屬性的匿名型別執行個體只等於其本身。
' prod1 and prod2 have no key values.
Dim prod1 = New With {.Name = "paperclips", .Price = 1.29}
Dim prod2 = New With {.Name = "paperclips", .Price = 1.29}
' The following line displays False, because prod1 and prod2 have no
' key properties.
Console.WriteLine(prod1.Equals(prod2))
' The following statement displays True because prod1 is equal to itself.
Console.WriteLine(prod1.Equals(prod1))
如果索引鍵屬性的值相等,則相同匿名型別的兩個執行個體相等。 下列範例說明如何測試是否相等。
Dim prod3 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim prod4 = New With {Key .Name = "paperclips", Key .Price = 1.29}
' The following line displays True, because prod3 and prod4 are
' instances of the same anonymous type, and the values of their
' key properties are equal.
Console.WriteLine(prod3.Equals(prod4))
Dim prod5 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim prod6 = New With {Key .Name = "paperclips", Key .Price = 1.29,
.OnHand = 423}
' The following line displays False, because prod5 and prod6 do not
' have the same properties.
Console.WriteLine(prod5.Equals(prod6))
Dim prod7 = New With {Key .Name = "paperclips", Key .Price = 1.29,
.OnHand = 24}
Dim prod8 = New With {Key .Name = "paperclips", Key .Price = 1.29,
.OnHand = 423}
' The following line displays True, because prod7 and prod8 are
' instances of the same anonymous type, and the values of their
' key properties are equal. The equality check does not compare the
' values of the non-key field.
Console.WriteLine(prod7.Equals(prod8))
唯讀值
您無法變更索引鍵屬性的值。 例如,在先前範例的 prod8
中,Name
和 Price
欄位為 read-only
,但 OnHand
可以變更。
' The following statement will not compile, because Name is a key
' property and its value cannot be changed.
' prod8.Name = "clamps"
' OnHand is not a Key property. Its value can be changed.
prod8.OnHand = 22
查詢運算式的匿名型別
查詢運算式不一定需要建立匿名型別。 可能的話,查詢運算式會使用現有的類型來保存資料行資料。 當查詢從資料來源傳回完整記錄,或每個記錄只傳回一個欄位時,就會發生這種情況。 在下列程式碼範例中,customers
是 Customer
類別的物件集合。 類別有許多屬性,且您可以依任何順序在查詢結果中包含一或多個屬性。 在前兩個範例中,因為查詢會選取具名類型的元素,所以不需要匿名型別:
因為
cust.Name
是字串,所以custs1
包含字串的集合。Dim custs1 = From cust In customers Select cust.Name
因為
customers
的每個元素都是Customer
物件,且查詢會選取整個元素,所以custs2
包含Customer
物件的集合。Dim custs2 = From cust In customers Select cust
不過,您並非一律能使用適當的具名類型。 您可能想要針對某個用途選取客戶姓名和地址、針對另一個用途選取客戶識別碼和位置,以及針對第三個用途選取客戶姓名、地址和訂單歷程記錄。 匿名型別可讓您依任何順序選取任何屬性組合,而不需先宣告新的具名類型來保存結果。 相反地,編譯器會為每個屬性的編譯建立匿名型別。 下列查詢只會從 customers
中的每個 Customer
物件選取客戶的名稱和識別碼。 因此,編譯器會建立只包含這兩個屬性的匿名型別。
Dim custs3 = From cust In customers
Select cust.Name, cust.ID
匿名型別中屬性的名稱和資料類型都是從引數擷取至 Select
、cust.Name
和 cust.ID
。 查詢所建立匿名型別中的屬性,一律為索引鍵屬性。 在下列 For Each
迴圈中執行 custs3
時,結果是匿名型別的執行個體集合,其中包含兩個索引鍵屬性:Name
和 ID
。
For Each selectedCust In custs3
Console.WriteLine(selectedCust.ID & ": " & selectedCust.Name)
Next
custs3
所呈現集合中的元素為強型別,您可以使用 IntelliSense 來瀏覽可用的屬性,並驗證其類型。
如需詳細資訊,請參閱 Visual Basic 中的 LINQ 簡介。
決定是否要使用匿名型別
在建立物件作為匿名類別的執行個體之前,請考慮這是否為最佳選項。 例如,如果您想要建立暫存物件來包含相關資料,且不需要整個類別所包含的其他欄位和方法,則匿名型別是很好的解決方案。 如果您想要針對每個宣告選擇不同的屬性,或想要變更屬性的順序,匿名型別也十分便利。 不過,如果您的專案依固定順序包含數個具有相同屬性的物件,則可以使用具名類型搭配類別建構函式來更輕鬆地加以宣告。 例如,使用適當的建構函式,會比宣告匿名型別的數個執行個體更容易宣告 Product
類別的數個執行個體。
' Declaring instances of a named type.
Dim firstProd1 As New Product("paperclips", 1.29)
Dim secondProd1 As New Product("desklamp", 28.99)
Dim thirdProd1 As New Product("stapler", 5.09)
' Declaring instances of an anonymous type.
Dim firstProd2 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim secondProd2 = New With {Key .Name = "desklamp", Key .Price = 28.99}
Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = 5.09}
具名類型的另一個優點是編譯器可以攔截意外誤用屬性名稱。 在先前範例中,firstProd2
、secondProd2
和 thirdProd2
是相同匿名型別的執行個體。 不過,如果您不小心以下列其中一種方式宣告 thirdProd2
,則其類型會與 firstProd2
和 secondProd2
的類型不同。
' Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = 5.09}
' Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = "5.09"}
' Dim thirdProd2 = New With {Key .Name = "stapler", .Price = 5.09}
更重要的是,使用不適用於具名類型執行個體的匿名型別有其限制。 firstProd2
、secondProd2
和 thirdProd2
是相同匿名型別的執行個體。 不過,您無法使用共用匿名型別的名稱,且不得出現在程式碼中類型名稱的應有位置。 例如,匿名型別無法用來定義方法特徵標記、宣告另一個變數或欄位,或出現在任何型別宣告中。 因此,當您必須跨方法共用資訊時,匿名型別並不適用。
匿名型別定義
為了回應匿名型別執行個體的宣告,編譯器會建立一個新的類別定義來包含指定屬性。
如果匿名型別至少包含一個索引鍵屬性,則定義會覆寫繼承自 Object 的三個成員:Equals、GetHashCode 和 ToString。 為測試相等並判斷雜湊碼值所產生的程式碼只會考慮索引鍵屬性。 如果匿名型別不包含索引鍵屬性,則只會覆寫 ToString。 匿名型別的明確命名屬性不能與這些產生的方法相衝突。 也就是說,您不能使用 .Equals
、.GetHashCode
或 .ToString
來命名屬性。
至少擁有一個索引鍵屬性的匿名型別定義也會實作 System.IEquatable<T> 介面,其中 T
是該匿名型別的類型。
如需編譯器所建立程式碼和覆寫方法功能的詳細資訊,請參閱匿名型別定義。