11 模式和模式比對
11.1 一般
模式是一種語法形式,可以搭配is
運算符 (^12.12.12) 和 switch_statement (^13.8.3) 來表示要比較內送數據的數據形狀。 模式會針對 switch 語句的表達式,或針對運算符左側is
的relational_expression進行測試,每個運算符都稱為模式輸入值。
11.2 模式表單
11.2.1 一般
模式可能具有下列其中一種形式:
pattern
: declaration_pattern
| constant_pattern
| var_pattern
;
declaration_pattern和var_pattern可能會導致局部變數的宣告。
每個模式表單都會針對可套用模式的輸入值定義一組類型。 如果 模式是模式可能相符值的型別之一,則模式P
適用於類型T
。T
如果程式中出現模式以符合類型的T
模式P
輸入值(~11.1),則為編譯時期錯誤,如果P
不適用於 T
。
範例:下列範例會產生編譯時間錯誤,因為的編譯時間類型
v
為TextReader
。 型TextReader
別的變數永遠不能有與string
參考兼容的值:TextReader v = Console.In; // compile-time type of 'v' is 'TextReader' if (v is string) // compile-time error { // code assuming v is a string }
不過,下列不會產生編譯時間錯誤,因為的編譯時間類型
v
為object
。 類型的object
變數可能會有與 參考相容的string
值:object v = Console.In; if (v is string s) { // code assuming v is a string }
end 範例
每個模式表單都會定義模式 符合 運行時間值的值集。
11.2.2 宣告模式
declaration_pattern是用來測試某個值具有指定型別,如果測試成功,請在該類型的變數中提供值。
declaration_pattern
: type simple_designation
;
simple_designation
: single_variable_designation
;
single_variable_designation
: identifier
;
值的運行時間類型會使用is-type運算子中指定的相同規則,針對模式中的型別進行測試(^12.12.12.12.12.1)。 如果測試成功,模式 會 符合該值。 如果 類型 是可為 Null 的實值型別,則為編譯時期錯誤(~8.3.12)。 此模式表單永遠不會符合 null
值。
注意:當 不是可為 Null 的類型時
T
,is-type 運算式e is T
和宣告模式e is T _
就相等。 end note
假設模式輸入值 (~11.1) e,如果simple_designation是標識符_
,則表示捨棄 #9.2.9.1),而 e 的值不會系結至任何值。 (雖然具有名稱 _
的宣告變數可能在該時間點的範圍內,但在此內容中看不到該具名變數。如果 simple_designation 為任何其他標識符,則會引進指定標識符所命名之指定型別的局部變數(~9.2.9)。 當模式符合值時,該局部變數會指派模式輸入值的值。
模式輸入值和指定型別的特定靜態類型組合會被視為不相容,並導致編譯時期錯誤。 如果存在身分識別轉換、隱含或明確參考轉換、Boxing 轉換或從 轉換為 的 Unboxing 轉換,或或為開放式類型 ,則靜態類型的E
值會與類型T
相容。。。。T
E
E
T
宣告模式命名類型T
適用於與 模式相容的每一種類型E
。T
E
注意:檢查可能是結構或類別類型的類型,並避免 Boxing 時,開啟類型的支援最有用。 end note
範例:宣告模式適用於執行參考型別的運行時間類型測試,並取代成語
var v = expr as Type; if (v != null) { /* code using v */ }
稍微精簡一點
if (expr is Type v) { /* code using v */ }
end 範例
如果 類型 為可為 Null 的實值類型,則為錯誤。
範例:宣告模式可用來測試可為 Null 型別的值:如果值為非 Null
T2
且 為T
,或是 的一些基底類型或介面T
,類型(或 BoxedT
) 的值Nullable<T>
會比對類型模式T2 id
。 例如,在代碼段int? x = 3; if (x is int v) { /* code using v */ }
語句的條件
if
是在true
運行時間,而變數v
會保留 區塊內的 型別int
值3
。 end 範例
11.2.3 常數模式
constant_pattern是用來測試模式輸入值(~11.1)與指定常數值的值。
constant_pattern
: constant_expression
;
如果 常數表達式從的常數表達式隱含轉換成 型別,則常數模式P
適用於 T
類型T
。P
對於常數模式 P
,其 轉換的值 為
- 如果模式輸入值的型別是整數類型或列舉類型,則模式的常數值會轉換成該類型;否則
- 如果模式輸入值的型別是整數型別或列舉類型的可為 Null 版本,則模式的常數值會轉換成其基礎類型;否則
- 模式常數值的值。
假設模式輸入值 e 和具有已轉換值 v 的常數模式P
,
- 如果 e 具有整數型別或列舉型別,或是其中一種可為 Null 的形式,且 v 具有整數類型,則模式會比對表達式
e == v
的結果為true
,則比P
對 e 值;否則為 - 如果 傳回 ,則模式
P
會比對 e 值。true
object.Equals(e, v)
範例:
switch
下列方法中的 語句會在其案例標籤中使用五個常數模式。static decimal GetGroupTicketPrice(int visitorCount) { switch (visitorCount) { case 1: return 12.0m; case 2: return 20.0m; case 3: return 27.0m; case 4: return 32.0m; case 0: return 0.0m; default: throw new ArgumentException(...); } }
end 範例
11.2.4 Var 模式
var_pattern 符合每個值。 也就是說,具有var_pattern的模式比對作業一律會成功。
var_pattern適用於每個類型。
var_pattern
: 'var' designation
;
designation
: simple_designation
;
指定模式輸入值 (~11.1) e,如果指定為標識符_
,則表示捨棄 #9.2.9.1),而 e 的值不會系結至任何值。 (雖然具有該名稱的宣告變數可能位於該時間點的範圍中,但在此內容中看不到該具名變數。如果指定是任何其他標識碼,在運行時間,e 的值會系結至該名稱的新引進局部變數 (~9.2.9),其類型為 e 的靜態類型,並將模式輸入值指派給該局部變數。
如果名稱var
系結至使用var_pattern的類型,就會發生錯誤。
11.3 模式子建議
在 switch 語句中,如果案例的模式 是由先前的未受保值案例集所細分 ,就會發生錯誤(~13.8.3)。 非正式地說,這表示任何輸入值都會與上述其中一個案例相符。 下列規則會定義一組模式子化指定模式的時機:
如果該模式的運行時間行為規格符合 P
,則模式P
會比對常數K
。K
如果下列任一條件保留,一組模式會Q
子化模式P
:
P
是常數模式,而且集合Q
中的任何模式都會符合P
已 轉換的值P
是 var 模式,而且模式集合Q
對於模式輸入值的類型而言是詳盡的 (~11.4),而且模式輸入值不是可為 Null 的類型,或 中的Q
某些模式會相符null
。P
是具有類型的T
宣告模式,而且一組模式Q
對於類型T
而言是詳盡的({11.4)。
11.4 模式詳盡
在非 Null 的每個可能值中,如果集合中的某些模式適用,則一組模式對於類型而言是詳盡的。 下列規則會定義類型的一組模式 何時詳盡 :
如果下列任一條件保留,一組模式 Q
會 詳盡說明 類型 T
:
T
是整數或列舉型別,或其中一個可為 Null 的版本,而且對於 's non-nullable 基礎型別的每個可能值T
而言,中的Q
某些模式會符合該值;或- 中的
Q
某些模式為 var 模式;或 - 中的
Q
某些模式是 類型的D
宣告模式,而且有識別轉換、隱含參考轉換,或從T
轉換為D
的Boxing轉換。
範例:
static void M(byte b) { switch (b) { case 0: case 1: case 2: ... // handle every specific value of byte break; // error: the pattern 'byte other' is subsumed by the (exhaustive) // previous cases case byte other: break; } }
end 範例