11 模式和模式比對

11.1 一般

模式可與is運算符(§12.15.12)、switch_statement(§13.8.3)及switch_expression(§12.12)使用,以描述資料的形狀,供輸入資料比較。 模式可以巢狀,資料部分會與 子模式進行比對。

模式會針對許多內容中的值進行測試:

  • 在switch_statement中,switch_label的圖案會與switch_statement的selector_expression進行測試。
  • 使用 is-模式 運算子,右側的 模式 會與左側的表達式進行檢驗。
  • 在switch_expression中,switch_expression_arm的模式會與switch_expression左側的表達式進行測試。
  • 在巢狀環境定義中,子 型樣 會針對從屬性、欄位擷取的值或從其他輸入值編製索引的值進行測試,視型樣形式而定。

測試型樣的值稱為 型樣輸入值。

若 的P任意輸入值被 的Q成員匹配,則該模式P被一組無守護模式Q所吸收。

在切換陳述(§13.8.3)中,若一個格的模式被前一組無守護(§13.8.3)格子所吞併,則該格子為錯誤。 在交換表達式(§12.12)中,若switch_expression_arm的模式被前一組未防護的 switch_expression_arms 模式所吸收,則為錯誤。

若對每個可能的輸入值,集合中某個模式都適用,則稱該模式集合是窮盡的。 當實作偵測到一組模式不完整時,應發出警告。

11.2 模式表單

11.2.1 一般

模式可能具有下列其中一種形式:

pattern
    : logical_pattern
    ;

primary_pattern
    : parenthesized_pattern
    | declaration_pattern
    | constant_pattern
    | var_pattern
    | positional_pattern
    | property_pattern
    | discard_pattern
    | type_pattern
    | relational_pattern
    | list_pattern
    | slice_pattern
    ;

parenthesized_pattern
    : '(' pattern ')'
    ;

產生允許 '(' pattern ')' 以括號包覆圖案,以強制組合於使用 logical_patterns 之一的圖案間的評估順序。

若輸入能在語法上同時被識別為 constant_pattern 與 positional_pattern ,則選擇該 constant_pattern 。

某些 型樣s 可能會導致局部變數的宣告。

每個模式表單都會針對可套用模式的輸入值定義一組類型。 如果 模式是模式可能相符值的型別之一,則模式P適用於類型T。T 如果程式中出現模式以符合類型的P模式輸入值(T),則為編譯時期錯誤,如果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 範例

每個模式表單都會定義模式 符合 運行時間值的值集。

在模式匹配過程中(呼叫 Deconstruct、屬性存取及調用 的 System.Runtime.CompilerServices.ITuple成員)的運算與副作用評估順序未明確說明。

11.2.2 宣告模式

declaration_pattern可用來測試值是否具有給定類型,如果測試成功,則選擇性地在該類型的變數中提供值。

declaration_pattern
    : type simple_designation
    ;
simple_designation
    : discard_designation
    | single_variable_designation
    ;
discard_designation
    : '_'
    ;
single_variable_designation
    : identifier
    ;

在承認 simple_designation 時,如果 discard_designation 與 single_variable_designation 兩種替代方案都適用,則應選擇前者。

注意:ANTLR 會自動做出指定的選擇,這是因為 simple_designation 的選項排序。 結尾註釋

如果 類型 是可為 Null 的值類型 (§8.3.12) 或可為 Null 的參考類型 (§8.9.3) ,則為編譯時間錯誤。

值的執行時型別會依照 is-type 運算子(§12.15.12.1)所規定的規則,與模式中的型別進行測試。 如果測試成功,模式 會 符合該值。

注意:當 is 型態 e is T 表達式與宣告模式 e is T _ 同時有效時,兩者是等價的。 結尾註釋

給定一個模式輸入值(§11.1) e,若 simple_designation 為 discard_designation,表示棄置(§9.2.9.2),則 e 的值不綁定於任何東西。 否則,若 simple_designation 為 single_variable_designation,則會引入一個由指定識別碼命名的區域變數(§9.2.9)。 當模式符合值時,該局部變數會指派模式輸入值的值。

注意:此 _ 內的處理與以_形式撰寫的獨立程式(§11.2.7)不同:後者中,作用域內常數或類型若_有 ,則不被隱藏。 結尾註釋

E若存在單位轉換、隱含或顯式參考轉換、盒裝轉換、開箱轉換,或隱含或顯式可空值型別從 轉換為 ,或T其中 或 E 為開放型態(T),則稱該型別與該型別E模式T。 命名型態T的宣告模式適用於(§11.2.1)所有與 E 模式相容E的型別T。 若使用宣告模式命名型T別來匹配靜態型態與模式相容E的模式輸入值(T),則編譯時即為錯誤。

注意:檢查可能是結構或類別類型的類型,並避免 Boxing 時,開啟類型的支援最有用。 結尾註釋

範例:宣告模式適用於執行參考型別的運行時間類型測試,並取代成語

var v = expr as Type;
if (v != null) { /* code using v */ }

稍微精簡一點

if (expr is Type v) { /* code using v */ }

end 範例

範例:宣告模式可用來測試可為 Null 型別的值:如果值為非 Null Nullable<T> 且 為 T,或是 的一些基底類型或介面T2 id,類型(或 BoxedT2) 的值T會比對類型模式T。 例如,在代碼段

int? x = 3;
if (x is int v) { /* code using v */ }

語句的條件if是在true運行時間,而變數v會保留 區塊內的 型別3值int。 在區塊之後,變數 v 在作用域內,但未明確指派。 end 範例

11.2.3 常數模式

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

constant_pattern
    : constant_expression
    ;

若常數表達式從 的P常數表達式隱含轉換到 T類型 ,或 T 是 System.Span<char> 或 System.ReadOnlySpan<char> 且 的P常數表達式為 類型string且非null文字,則該常數模式P適用於型態T。

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

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

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

  • 如果 e 具有整數型別或列舉型別,或是其中一種可為 Null 的形式,且 v 具有整數類型,則模式會比對表達式P的結果為 ,則比e == v對 etrue;否則為
  • 若 e 型別System.Span<char>為 或 System.ReadOnlySpan<char>,且 v 是常數字串,且 v 沒有常數值 ,nullP則模式與 e相符,若System.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(v))回傳 true;否則
  • 如果 傳回 ,則模式P會比對 e 值。object.Equals(e, v)true

範例: 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
    | tuple_designation
    ;
tuple_designation
    : '(' designations? ')'
    ;
designations
    : designation (',' designation)*
    ;

給定模式輸入值 (§11.1) e,如果指定discard_designation,則表示捨棄 (§9.2.9.2),且 e 的值不繫結於任何內容。 (雖然具有該名稱的宣告變數可能在該點範圍內,但在此內容中看不到該具名變數。否則,如果指定single_variable_designation,則在執行階段,e 的值會系結至該名稱的新引進的區域變數 (§9.2.9),其類型是 e 的靜態類型,而模式輸入值會指派給該區域變數。

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

如果名稱是tuple_designation,則該圖案相當於形式名稱的(var (§11.2.5),... ) 其中 名稱s 是在 tuple_designation內找到的名稱。 例如,模式var (x, (y, z))(var x, (var y, var z))相當於 。

11.2.5 位置模式

positional_pattern檢查輸入值是否為 null,從中提取一連串數值,並將每個取出的值與相應子模式比對。 這些值可透過三種方式之一提取:將輸入視為元組、 Deconstruct 呼叫方法,或透過 System.Runtime.CompilerServices.ITuple索引輸入。

注意:此處的 Deconstruct 使用與 第12.7節定義的源層級解構轉換不同。 結尾註釋

positional_pattern
    : type? '(' subpatterns? ')' property_subpattern? simple_designation?
    ;
subpatterns
    : subpattern (',' subpattern)*
    ;
subpattern
    : pattern
    | subpattern_name ':' pattern
    ;
subpattern_name
    : identifier
    | subpattern_name '.' identifier
    ;

設 n 為括號間出現的 子模式s 數量。 配對策略於編譯時依序應用以下情況來選擇;使用第一個條件符合的情況,其餘情況不予考慮。 一旦選定案例,該策略即被執行:該案例中所述的任何編譯時錯誤都會被報告,且匹配不會延續到後續案例。

  1. 元組形式。 若型別省略且輸入值的靜態型態為元組型別(§8.3.11),或輸入值為元組字面值(§12.8.6),則此情況適用。 若 n 不等於該元組的元數,則稱為編譯時錯誤。 執行時,每個元組元素會與相應 的子模式匹配;若所有這些成功,該配對即告成功。 若任何 子模式 有 識別碼,該 識別 碼應命名該元組類型中對應位置的元組元素。
  2. 解構形態。 否則,若任一 型 別存在,或型 別 被省略且輸入值的靜態型別包含可存取 Deconstruct 方法(§12.7),則此情況適用。 若 存在型態,則 D 為型態;否則設 D 為輸入值的靜態型態。 Deconstruct從 D 中選擇方法時,使用與解構宣告相同的超載解析規則,並額外要求其參數數out必須等於 n;若不存在此類方法,則稱為編譯時錯誤。 若存在型別,則若輸入值的靜態型別與型別不相容(§11.2.2),則為編譯時錯誤;執行時會對輸入值進行型別測試,若測試失敗,位置模式匹配即告失敗。 否則,輸入值會轉換成 D,並以新變數接收所Deconstruct選out方法的參數。 每個接收值都會與相應 子模式匹配,若所有子模式都成功,則匹配成功。 若任何 子模式 有 識別碼,該 識別 碼應命名該參數,對應位置 Deconstruct。
  3. 雙重形式。 否則 ,若型 別被省略、子 模式 無 識別碼,且輸入值的靜態型態為 object、 System.Runtime.CompilerServices.ITuple,或有隱含參考轉換至 System.Runtime.CompilerServices.ITuple的型別,則此情況適用。 執行時會測試輸入值是否非null 的 System.Runtime.CompilerServices.ITuple實例;若測試失敗,位置模式匹配即告失敗。 否則,讀取該值的 Length 性質,若不等於 n,則位置模式匹配失敗。 否則,對於每個 i 從 1 到 n 的值,將輸入值索引為 i − 1 所得到的值,會與 第 i個子模式匹配,若所有子模式皆成功,則匹配成功。
  4. 否則,不適用任何案例, positional_pattern 為編譯時錯誤。

未指定執行時期比對子型樣的順序,且失敗的比對可能不會嘗試比對所有子型樣。

範例:在這裡,我們解構一個表達式結果,並將結果值與對應的巢狀模式進行比對:

static string Classify(Point point) => point switch
{
    (0, 0) => "Origin",
    (1, 0) => "positive X basis end",
    (0, 1) => "positive Y basis end",
    _ => "Just a point",
};

public readonly struct Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) => (X, Y) = (x, y);
    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

end 範例

範例:元組元素和解構參數的名稱可以在位置模式中使用,如下所示:

var numbers = new List<int> { 10, 20, 30 };
if (SumAndAverage(numbers) is (Sum: var sum, Average: var average))
{
    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}; average is {average}");
}
else
{
    // Note: sum and average are in scope here, but not definitely assigned
    Console.WriteLine("No numbers provided to compute sum and average.");   
}

static (double Sum, double Average)? SumAndAverage(IEnumerable<int> numbers)
{
    int sum = 0;
    int count = 0;
    foreach (int number in numbers)
    {
        sum += number;
        count++;
    }
    return count == 0 ? null : (sum, sum / count);
}

產生的輸出是

Sum of [10 20 30] is 60; average is 20

end 範例

11.2.6 屬性模式

property_pattern會檢查輸入值是否不是 null,並遞迴比對使用可存取屬性或欄位所擷取的值。

property_pattern
    : type? property_subpattern simple_designation?
    ;
property_subpattern
    : '{' '}'
    | '{' subpatterns ','? '}'
    ;

若property_pattern的子模式不包含subpattern_name,則為錯誤。

如果 類型 是可為 Null 的值類型 (§8.3.12) 或可為 Null 的參考類型 (§8.9.3) ,則為編譯時間錯誤。

附註: 空值檢查模式會脫離簡單的屬性模式。 若要檢查字串 s 是否為非空值,可以編寫下列任何形式:

#nullable enable
string s = "abc";
if (s is object o) ...  // o is of type object
if (s is string x1) ... // x1 is of type string
if (s is {} x2) ...     // x2 is of type string
if (s is {}) ...

宣告 x2 的範例在推斷變數類型方面類似 if (s is var x2) ,但屬性模式保證 非 x2 空。 結尾註釋

給定表達式 e 與模式型{別子模式}的匹配,若表達式 e 與型別指定的型別 T 不相容(§11.2.2),則為編譯時錯誤。 如果類型不存在,則假設類型是 e 的靜態類型。 每個出現在其子圖案左側的subpattern_name,都應指定一個可讀取的 T 屬性或域。如果property_pattern的simple_designation存在,則宣告一個型態為 T 的模式變數。

在執行階段,會針對 T 測試運算式。如果失敗,則內容型樣比對會失敗,結果為 false。 如果成功,則會讀取每個 property_subpattern 欄位或屬性,並將其值與其對應的模式進行比對。 只有當其中任何一個的結果為 false時,整個比賽的結果才算false。 未指定子型樣比對的順序,且失敗的比對可能不會在執行時期測試所有子型樣。 如果比對成功,且property_pattern的simple_designation是single_variable_designation,則會為宣告的變數指派比對值。

property_pattern可用來與匿名類型進行模式比對。

subpattern_name可能會參考巢狀成員。 在這種情況下,每次名稱查詢的接收器是前一個成員 T₀ 的型別,從 property_pattern 的輸入型態開始。 若 T 是可空型,則 T₀ 為其底層型別,否則 T₀ 等於 T。例如,形式的 { Prop1.Prop2: pattern } 圖案恰好等價於 { Prop1: { Prop2: pattern } }。

注意:當 T 是可空值型別或參考型別時,這會包含空檢查。 此空檢查表示巢狀性質將是 T₀ 的性質,而非 T 的。由於允許重複成員路徑,模式匹配的編譯可以利用模式的共同部分。 結尾註釋

範例:

var o = ...;
if (o is string { Length: 5 } s) ...

end 範例

範例:執行時期類型檢查和變數宣告可以新增至屬性模式,如下所示:

Console.WriteLine(TakeFive("Hello, world!"));  // output: Hello
Console.WriteLine(TakeFive("Hi!"));            // output: Hi!
Console.WriteLine(TakeFive(new[] { '1', '2', '3', '4', '5', '6', '7' }));  // output: 12345
Console.WriteLine(TakeFive(new[] { 'a', 'b', 'c' }));  // output: abc

static string TakeFive(object input) => input switch
{
    string { Length: >= 5 } s => s.Substring(0, 5),
    string s => s,
    ICollection<char> { Count: >= 5 } symbols => new string(symbols.Take(5).ToArray()),
    ICollection<char> symbols => new string(symbols.ToArray()),
    null => throw new ArgumentNullException(nameof(input)),
    _ => throw new ArgumentException("Not supported input type."),
};

產生的輸出是

Hello
Hi!
12345
abc

end 範例

11.2.7 丟棄模式

每個運算式都符合捨棄模式,這會導致捨棄運算式的值。

discard_pattern
    : '_'
    ;

若語法上下文允許模式,若標記_可解析為可存取常數或型別的simple_name(§12.8.4),則_不被視為discard_pattern。 相反:

  • 若 _ 解析為可達常數, _ 則解釋為一個 constant_pattern (§11.2.3),其常數表達式即為該常數。
  • 若 _ 解析為某型態,則在運算 is 子右側,構念 relational_expressionis _ 被解釋為 is 型運算子(§12.15.12.1),以測試該型別。 在任何其他允許模式的語法語境中,_單純解析為型別本身並不是一個有效的模式;然而,可以_作為declaration_pattern的型別(例如 _ x),或在其他明確命名型別的型態形式中出現。

此規則保留了與在引入丟棄模式前定義 _ 為類型或識別碼的程式碼的向下相容性。 若 _ 解析為非可存取的常數或型別(例如區域變數、參數、欄位或方法),則該規則不適用,仍 _ 維持 discard_pattern。

注意:這與var中的規則類似,不同之處在於_範圍內的常數或類型將導致_解釋為該宣告的參考,而非產生錯誤。 結尾註釋

若套用前述規則後,該標記_仍為discard_pattern,則該discard_pattern若呈現為relational_expression型態的整個is,或switch_label的整個模式,則屬於編譯時錯誤。 然而,discard_pattern也可能出現為包圍圖案的子圖案(例如,作為positional_pattern或property_pattern的子圖案)。

注意:在這些情況下,若要比對任何運算式,請使用帶有捨棄的var _。 結尾註釋

範例:

Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday));
Console.WriteLine(GetDiscountInPercent(null));
Console.WriteLine(GetDiscountInPercent((DayOfWeek)10));

static decimal GetDiscountInPercent(DayOfWeek? dayOfWeek) => dayOfWeek switch
{
    DayOfWeek.Monday => 0.5m,
    DayOfWeek.Tuesday => 12.5m,
    DayOfWeek.Wednesday => 7.5m,
    DayOfWeek.Thursday => 12.5m,
    DayOfWeek.Friday => 5.0m,
    DayOfWeek.Saturday => 2.5m,
    DayOfWeek.Sunday => 2.0m,
    _ => 0.0m,
};

產生的輸出是

5.0
0.0
0.0

此處使用棄置模式處理 null 及 與 任何不包含列舉對應成員 DayOfWeek 的整數值。 這保證 switch 運算式處理所有可能的輸入值。 end 範例

範例:以下說明一個在作用域內命名_的常數如何改變表達式中_對switch臂的解釋。 在 WithoutUnderscore中, _ 臂是 discard_pattern ,且可匹配任意值。 在 WithUnderscore中,作用域內常數 _ 使 _ 臂被解釋為僅匹配 的 值0的 constant_pattern 。

static string WithoutUnderscore(int n) => n switch
{
    1 => "one",
    _ => "other",
};

static string WithUnderscore(int n)
{
    const int _ = 0;
    return n switch
    {
        1 => "one",
        _ => "zero",
        var x => "other: " + x,
    };
}

end 範例

11.2.8 類型模式

type_pattern用來測試模式輸入值(§11.1)是否具有特定類型。

type_pattern
    : type
    ;

命名型別T的型態模式適用於所有與 (E) 模式E 的型別T。

值的執行時型別 會依照 is-type 運算子(§12.15.12.1)中指定的相同規則來測試。 如果測試成功,該模式會與該值相符。 若型 別 是可空型,則稱為編譯時錯誤。 此模式表單永遠不會符合 null 值。

11.2.9 關係模式

relational_pattern用於對模式輸入值(§11.1)與常數值進行關係性測試。

relational_pattern
    : '<'  relational_expression
    | '<=' relational_expression
    | '>'  relational_expression
    | '>=' relational_expression
    ;

relational_pattern中的relational_expression必須被計算為常數值。

關聯型態支援關聯運算子 、 、 ,以及所有內建支援此類二元關聯運算子且兩個運算元類型相同類型的類型:<、 、 <=>>=sbytebyteshortushortintuintlongulongcharfloatdoubledecimalnintnuint

若定義一個合適的內建二元關係運算子,且兩個運算元類型為 ,或存在從常數表達式類型 的顯式可空化或開箱轉換,則該T可T該型態T。

若運算式應值為 double.NaN、 float.NaN或為空常數,則為編譯時錯誤。

當輸入值有一個類型,並定義了適當的內建二元關係運算子時,該運算符的評估即視為關聯模式的意義。 否則,輸入值會透過顯式可空化或開箱轉換轉換為常數表達式的型別。 如果沒有任何這類轉換存在,則為編譯時期錯誤。 若轉換失敗,該模式被視為不匹配。 若轉換成功,模式匹配運算的結果即為計算 e «op» v 表達式的結果,其中 e 為轉換後的輸入,「op」為關聯運算子, v 為常數表達式。

範例:

Console.WriteLine(Classify(13));
Console.WriteLine(Classify(double.NaN));
Console.WriteLine(Classify(2.4));

static string Classify(double measurement) => measurement switch
{
    < -4.0 => "Too low",
    > 10.0 => "Too high",
    double.NaN => "Unknown",
    _ => "Acceptable",
};

產生的輸出是

Too high
Unknown
Acceptable

end 範例

11.2.10 邏輯模式

logical_pattern用於抵消模式匹配結果,或利用合取(and)或析取(析取or)組合多個模式匹配結果。

logical_pattern
    : disjunctive_pattern
    ;

disjunctive_pattern
    : disjunctive_pattern 'or' conjunctive_pattern
    | conjunctive_pattern
    ;

conjunctive_pattern
    : conjunctive_pattern 'and' negated_pattern
    | negated_pattern
    ;

negated_pattern
    : 'not' negated_pattern
    | primary_pattern
    ;

not、 and、 以及 or 統稱為 模式算符。

若被否定的模式不匹配,則 negated_pattern 匹配,反之亦然。 conjunctive_pattern需要兩種圖案相匹配。 disjunctive_pattern需要任一圖案相匹配。 與語言運算子不同, && 且 ||、 andor和 並非 短路運算子。

在 OR not 模式運算子下宣告or模式變數時,這會造成編譯時的錯誤。

注意:由於 和 notor 都無法為模式變數產生明確指派,因此在這些位置宣告 Reference 變數是錯誤的。 結尾註釋

在conjunctive_pattern中,第二個模式的輸入型態會被第一個模式的and要求所縮小。 模式的狹義型P定義如下:

  • 若 P 是型態型態,則 狹義型 態即為該型態型態的型態。
  • 否則,若 P 是宣告模式,則 縮窄型態 即為宣告模式型態的型別。
  • 否則,若 P 是一個遞迴模式,且給出明確型態,則窄 義型態 即為該型別。
  • 否則,若 P 依 ITuple 中的規則(§11.2.5)匹配,則縮小型態為 System.ITuple。
  • 否則,若 P 是一個常數模式,且常數不是零常數,且表達式沒有常數表達式轉換為輸入型態,則窄化型態即為常數型態。
  • 否則,若 P 是一個關係型態,且常數表達式沒有常 數表達 式轉換為 輸入型態,則 窄化型 態即為常數的型別。
  • 否則,若 P 是一個 or 模式,則該 窄化型 別即為子型態中 該窄型的 共同型態(若存在此類共同型)。 為此,通用型態演算法僅考慮身份、盒裝及隱含參考轉換,並考慮一連串 or 模式的所有子模式(忽略括號內模式)。
  • 否則,若 P 是模式 and ,則 窄型 即為正確模式的 窄型 。 此外,左圖樣的 窄化型 是右圖樣的 輸入型 態。
  • 否則,的P是P輸入型態。

註:如文法所示, not 先於 and,而 又優先於 or。 這點可以用括號明確標示或覆蓋。 結尾註釋

當 模式出現在 右側 is時,模式的範圍由文法決定;因此,模式運算元 and、 or、 在 not 模式內的結合比邏輯運算 &&子 、 ||、 、 ! 更緊密,且在模式之外。

範例:

Console.WriteLine(Classify(13));
Console.WriteLine(Classify(-100));
Console.WriteLine(Classify(5.7));

static string Classify(double measurement) => measurement switch
{
    < -40.0 => "Too low",
    >= -40.0 and < 0 => "Low",
    >= 0 and < 10.0 => "Acceptable",
    >= 10.0 and < 20.0 => "High",
    >= 20.0 => "Too high",
    double.NaN => "Unknown",
};

產生的輸出是

High
Too low
Acceptable

end 範例

範例:

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));

static string GetCalendarSeason(DateTime date) => date.Month switch
{
    3 or 4 or 5 => "spring",
    6 or 7 or 8 => "summer",
    9 or 10 or 11 => "autumn",
    12 or 1 or 2 => "winter",
    _ => throw new ArgumentOutOfRangeException(nameof(date),
      $"Date with unexpected month: {date.Month}."),
};

產生的輸出是

winter
autumn
spring

end 範例

範例:

object msg = "msg";
object obj = 5;
bool flag = true;

// This is parsed as: (msg is (not int) or string)
bool result = msg is not int or string;
Console.WriteLine($"msg (\"msg\"): msg is not int or string: {result}");

// This is parsed as: (obj is (int or string)) && flag
result = obj is int or string && flag;
Console.WriteLine($"obj (5), flag (true): obj is int or string && flag: {result}");

// This is parsed as: (obj is int) || ((obj is string) && flag)
result = obj is int || obj is string && flag;
Console.WriteLine($"obj (5), flag (true): obj is int || obj is string && flag: {result}");

flag = false;
// This is parsed as: (obj is (int or string)) && flag
result = obj is int or string && flag;
Console.WriteLine($"obj (5), flag (false): obj is int or string && flag: {result}");

// This is parsed as: (obj is int) || ((obj is string) && flag)
result = obj is int || obj is string && flag;
Console.WriteLine($"obj (5), flag (false): obj is int || obj is string && flag: {result}");

產生的輸出是

msg ("msg"): msg is not int or string: True
obj (5), flag (true): obj is int or string && flag: True
obj (5), flag (true): obj is int || obj is string && flag: True
obj (5), flag (false): obj is int or string && flag: False
obj (5), flag (false): obj is int || obj is string && flag: True

end 範例

11.2.11 列表模式

list_pattern 與列表或陣列中的一串元素相匹配。

list_pattern
    : list_pattern_clause simple_designation?
    ;

list_pattern_clause
    : '[' (pattern (',' pattern)* ','?)? ']'
    ;

list_pattern 與任何可數型別(§18.1)及可索引型別(§18.1)相容——它有一個可存取的索引器,該索引器以 為Index參數,或是具有單一int參數的可存取索引器。 若兩個索引器同時存在,則優先採用前者。 (隱含指標支持的細節請參見 第18.4.2 節。)

該形式的 expr is [1, 2, 3] 模式相當於以下代碼:

expr.Length is 3
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Index(1, fromEnd: false)] is 2
&& expr[new Index(2, fromEnd: false)] is 3

範例:

int[] numbers = { 1, 2, 3 };

Console.WriteLine(numbers is [1, 2, 3]);  // True
Console.WriteLine(numbers is [1, 2, 4]);  // False
Console.WriteLine(numbers is [1, 2, 3, 4]);  // False
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3 and not 7]);  // True

end 範例

棄牌模式(§11.2.7)可匹配任何單一元素。

範例:

List<int> numbers = new() { 1, 2, 3 };

if (numbers is [_, var second, _])
{
    Console.WriteLine($"The second element is {second}.");
}

end 範例

11.2.12 切片模式

slice_pattern會丟棄零個或多個元素。 該詞僅可直接在 list_pattern_clause中使用,且最多只能在該條款中使用一次。

slice_pattern
    : '..' pattern?
    ;

沒有子模式的 slice_pattern 與任何與 list_pattern相容的型別相容。 具有子模式的slice_pattern與任何可數型別(§18.1)及可切片型別(§18.1)相容——它有一個可存取的索引器,將 a Range 作為參數,或是具有兩個Slice參數的可存取int方法。 若兩者皆有,則優先選擇前者。 (隱含指標支持的細節請參見 第18.4.2 節。)

slice_pattern就像是真正的棄牌;也就是說,不得對該模式進行任何測試。 它只影響其他節點,也就是長度和索引器。 例如,形式的模式expr is [1, .. var s, 3]等價於以下程式碼(若透過明確與IndexRange支援相容):

expr.Length is >= 2
&& expr[new Index(0, fromEnd: false)] is 1
&& expr[new Range(new Index(1, fromEnd: false), new Index(1, fromEnd: true))] is var s
&& expr[new Index(1, fromEnd: true)] is 3

slice_pattern的輸入型別是底層this[Range]或Slice方法的回傳型別,但有兩個例外:對於 strings 和陣 string.SubstringRuntimeHelpers.GetSubArray列,分別應使用 ,以及 。

範例:切片模式可用於僅匹配輸入序列開始或/及結尾的元素。

Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]);  // True
Console.WriteLine(new[] { 1, 1 } is [_, _, ..]);  // True
Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]);  // False
Console.WriteLine(new[] { 1 } is [1, 2, ..]);  // False

Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]);  // True
Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]);  // False
Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]);  // True

Console.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]);  // True
Console.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]);  // True
Console.WriteLine(new[] { 1, 0, 1 } is [1, 0, .., 0, 1]);  // False

end 範例

範例:子模式可以嵌套在切片圖案中:

MatchMessage("aBBA");  // output: Message aBBA matches; inner part is BB.
MatchMessage("apron"); // output: Message apron doesn't match.

void MatchMessage(string message)
{
    var result = message is ['a' or 'A', .. var s, 'a' or 'A']
        ? $"Message {message} matches; inner part is {s}."
        : $"Message {message} doesn't match.";
    Console.WriteLine(result);
}

Validate(new[] { -1, 0, 1 });     // output: not valid
Validate(new[] { -1, 0, 0, 1 });  // output: valid

void Validate(int[] numbers)
{
    var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0]
        ? "valid" : "not valid";
    Console.WriteLine(result);
}

end 範例