共用方式為


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適用於類型TT 如果程式中出現模式以符合類型的T模式P輸入值(~11.1),則為編譯時期錯誤,如果P不適用於 T

範例:下列範例會產生編譯時間錯誤,因為的編譯時間類型 vTextReader。 型 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
}

不過,下列不會產生編譯時間錯誤,因為的編譯時間類型 vobject。 類型的 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.1e,如果simple_designation標識符_,則表示捨棄 #9.2.9.1),而 e 的值不會系結至任何值。 (雖然具有名稱 _ 的宣告變數可能在該時間點的範圍內,但在此內容中看不到該具名變數。如果 simple_designation 為任何其他標識符,則會引進指定標識符所命名之指定型別的局部變數(~9.2.9)。 當模式符合值時,該局部變數會指派模式輸入值的值。

模式輸入值和指定型別的特定靜態類型組合會被視為不相容,並導致編譯時期錯誤。 如果存在身分識別轉換、隱含或明確參考轉換、Boxing 轉換或從 轉換為 的 Unboxing 轉換,或或為開放式類型 ,則靜態類型的E值會與類型T相容。。T E E T 宣告模式命名類型T適用於與 模式相容的每一種類型ET 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會保留 區塊內的 型別int3end 範例

11.2.3 常數模式

constant_pattern是用來測試模式輸入值(~11.1)與指定常數值的值。

constant_pattern
    : constant_expression
    ;

如果 常數表達式從的常數表達式隱含轉換成 型別,則常數模式P適用於 T 類型TP

對於常數模式 P,其 轉換的值

  • 如果模式輸入值的型別是整數類型或列舉類型,則模式的常數值會轉換成該類型;否則
  • 如果模式輸入值的型別是整數型別或列舉類型的可為 Null 版本,則模式的常數值會轉換成其基礎類型;否則
  • 模式常數值的值。

假設模式輸入值 e 和具有已轉換值 v 的常數模式P

  • 如果 e 具有整數型別或列舉型別,或是其中一種可為 Null 的形式,且 v 具有整數類型,則模式會比對表達式e == v的結果為 true,則比P對 e;否則為
  • 如果 傳回 ,則模式P會比對 etrueobject.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.1e,如果指定標識符_,則表示捨棄 #9.2.9.1),而 e 的值不會系結至任何值。 (雖然具有該名稱的宣告變數可能位於該時間點的範圍中,但在此內容中看不到該具名變數。如果指定是任何其他標識碼,在運行時間,e 的值會系結至該名稱的新引進局部變數 (~9.2.9),其類型為 e靜態類型,並將模式輸入值指派給該局部變數。

如果名稱var系結至使用var_pattern的類型,就會發生錯誤。

11.3 模式子建議

在 switch 語句中,如果案例的模式 是由先前的未受保值案例集所細分 ,就會發生錯誤(~13.8.3)。 非正式地說,這表示任何輸入值都會與上述其中一個案例相符。 下列規則會定義一組模式子化指定模式的時機:

如果該模式的運行時間行為規格符合 P ,則模式P會比對常數KK

如果下列任一條件保留,一組模式會Q子化模式P

  • P 是常數模式,而且集合 Q 中的任何模式都會符合 P轉換的值
  • P是 var 模式,而且模式集合Q對於模式輸入值的類型而言是詳盡的 (~11.4),而且模式輸入值不是可為 Null 的類型,或 中的Q某些模式會相符null
  • P是具有類型的T宣告模式,而且一組模式Q對於類型T而言是詳盡的({11.4)。

11.4 模式詳盡

在非 Null 的每個可能值中,如果集合中的某些模式適用,則一組模式對於類型而言是詳盡的。 下列規則會定義類型的一組模式 何時詳盡

如果下列任一條件保留,一組模式 Q詳盡說明 類型 T

  1. T 是整數或列舉型別,或其中一個可為 Null 的版本,而且對於 's non-nullable 基礎型別的每個可能值 T而言,中的 Q 某些模式會符合該值;或
  2. 中的 Q 某些模式為 var 模式;或
  3. 中的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 範例