Vzory (Referenční dokumentace jazyka C#)
Jazyk c# představil porovnávání vzorů v jazyce C# 7,0. Od té doby každá hlavní verze jazyka C# rozšiřuje možnosti porovnávání vzorů. Následující výrazy a příkazy jazyka C# podporují porovnávání vzorů:
isvyjádřeníswitchpříkazswitchvýraz (představený v C# 8,0)
V těchto konstrukcích můžete porovnávat vstupní výraz s některým z následujících vzorů:
- Vzor deklarace: Pokud chcete kontrolovat typ běhu výrazu a, pokud je shoda úspěšná, přiřaďte výsledek výrazu deklarované proměnné. Představeno v C# 7,0.
- Typ Pattern: pro kontrolu typu běhu výrazu. Představeno v C# 9,0.
- Konstantní vzorek: k otestování, jestli výsledek výrazu odpovídá zadané konstantě. Představeno v C# 7,0.
- Relační vzory: pro porovnání výsledku výrazu se zadanou konstantou. Představeno v C# 9,0.
- Logické vzory: k otestování, zda výraz odpovídá logické kombinaci vzorků. Představeno v C# 9,0.
- Vzor vlastnosti: pro otestování, zda vlastnosti nebo pole výrazu odpovídají vnořeným vzorům. Představeno v C# 8,0.
- Poziční vzorek: k dekonstrukci výsledku výrazu a testování, pokud výsledné hodnoty odpovídají vnořeným vzorům. Představeno v C# 8,0.
-
varvzor: pro vyhledání výrazu a přiřazení výsledku k deklarované proměnné. Představeno v C# 7,0. - Vypustit vzor: pro vyhledání výrazu. Představeno v C# 8,0.
Logickévzorce, vlastnostia poziční vzory jsou rekurzivní vzory. To znamená, že mohou obsahovat vnořené vzory.
Příklad použití těchto vzorů k vytvoření algoritmu založeného na datech naleznete v tématu kurz: použití porovnávání vzorů pro sestavení a algoritmy řízené daty.
Deklarace a vzory typů
Pomocí deklarace a vzorů typů můžete zjistit, jestli je běhový typ výrazu kompatibilní s daným typem. Pomocí vzoru deklarace můžete také deklarovat novou místní proměnnou. Když vzor deklarace odpovídá výrazu, je této proměnné přiřazen výsledek převedeného výrazu, jak ukazuje následující příklad:
object greeting = "Hello, World!";
if (greeting is string message)
{
Console.WriteLine(message.ToLower()); // output: hello, world!
}
Počínaje jazykem C# 7,0 vzor deklarace s typem T odpovídá výrazu, pokud výsledek výrazu není null a je splněna kterákoli z následujících podmínek:
Běhový typ výsledku výrazu je
T.Běhový typ výsledku výrazu je odvozený od typu
T, implementuje rozhraníTnebo jiný implicitní převod odkazu z něj existuje naT. Následující příklad ukazuje dva případy, pokud je tato podmínka pravdivá:var numbers = new int[] { 10, 20, 30 }; Console.WriteLine(GetSourceLabel(numbers)); // output: 1 var letters = new List<char> { 'a', 'b', 'c', 'd' }; Console.WriteLine(GetSourceLabel(letters)); // output: 2 static int GetSourceLabel<T>(IEnumerable<T> source) => source switch { Array array => 1, ICollection<T> collection => 2, _ => 3, };V předchozím příkladu při prvním volání
GetSourceLabelmetody je první vzor shodný s hodnotou argumentu, protože typ modulu runtime argumentuint[]je odvozen z Array typu. Při druhém voláníGetSourceLabelmetody není typ modulu runtime argumentu List<T> odvozen od Array typu, ale implementuje ICollection<T> rozhraní.Běhový typ výsledku výrazu je typ hodnoty Nullable s podkladovým typem
T.Převod zabalení nebo rozbalení z typu modulu runtime pro výsledek výrazu na typ se nachází v době běhu
T.
Následující příklad demonstruje poslední dvě podmínky:
int? xNullable = 7;
int y = 23;
object yBoxed = y;
if (xNullable is int a && yBoxed is int b)
{
Console.WriteLine(a + b); // output: 30
}
Chcete-li kontrolovat pouze typ výrazu, můžete použít zahození _ namísto názvu proměnné, jak ukazuje následující příklad:
public abstract class Vehicle {}
public class Car : Vehicle {}
public class Truck : Vehicle {}
public static class TollCalculator
{
public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
{
Car _ => 2.00m,
Truck _ => 7.50m,
null => throw new ArgumentNullException(nameof(vehicle)),
_ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
};
}
Počínaje jazykem C# 9,0 pro tento účel můžete použít vzor typu, jak ukazuje následující příklad:
public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
{
Car => 2.00m,
Truck => 7.50m,
null => throw new ArgumentNullException(nameof(vehicle)),
_ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
};
Podobně jako vzor deklarace, vzorek typu odpovídá výrazu, pokud výsledek výrazu není null a jeho typ běhu splňuje některou z výše uvedených podmínek.
Další informace naleznete v částech vzor deklarace a typ vzorku v poznámkách k návrhu funkcí.
Konstantní vzorek
Počínaje jazykem C# 7,0 použijete konstantní vzorek k otestování, zda výsledek výrazu odpovídá zadané konstantě, jak ukazuje následující příklad:
public static decimal GetGroupTicketPrice(int visitorCount) => visitorCount switch
{
1 => 12.0m,
2 => 20.0m,
3 => 27.0m,
4 => 32.0m,
0 => 0.0m,
_ => throw new ArgumentException($"Not supported number of visitors: {visitorCount}", nameof(visitorCount)),
};
V konstantním vzoru můžete použít libovolný konstantní výraz, například:
- číselný literál nebo číslo s plovoucí desetinnou čárkou
- literál typu char nebo String
- Logická hodnota
truenebofalse - hodnota výčtu
- název deklarovaného pole const nebo místní
null
Použijte pro kontrolu konstantní vzorek null , jak ukazuje následující příklad:
if (input is null)
{
return;
}
Kompilátor garantuje, že == při vyhodnocení výrazu není vyvolán žádný přetížený operátor rovnosti uživatele x is null .
Počínaje jazykem C# 9,0 můžete použít model konstanty typu negace pro null kontrolu nenulové hodnoty, jak ukazuje následující příklad:
if (input is not null)
{
// ...
}
Další informace naleznete v části konstanta Pattern poznámky k návrhu funkcí.
Relační vzory
Počínaje jazykem C# 9,0 použijete relační vzor k porovnání výsledku výrazu s konstantou, jak ukazuje následující příklad:
Console.WriteLine(Classify(13)); // output: Too high
Console.WriteLine(Classify(double.NaN)); // output: Unknown
Console.WriteLine(Classify(2.4)); // output: Acceptable
static string Classify(double measurement) => measurement switch
{
< -4.0 => "Too low",
> 10.0 => "Too high",
double.NaN => "Unknown",
_ => "Acceptable",
};
V relačním vzoru můžete použít kterýkoli z relačních operátorů < , > , <= nebo >= . Pravá část relačního vzoru musí být konstantní výraz. Konstantní výraz může být celé číslo, plovoucídesetinná čárka, znaknebo Výčtový typ.
Chcete-li zjistit, zda je výsledek výrazu v určitém rozsahu, porovnejte jej se and vzorem conjunctive, jak ukazuje následující příklad:
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 3, 14))); // output: spring
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 7, 19))); // output: summer
Console.WriteLine(GetCalendarSeason(new DateTime(2021, 2, 17))); // output: winter
static string GetCalendarSeason(DateTime date) => date.Month switch
{
>= 3 and < 6 => "spring",
>= 6 and < 9 => "summer",
>= 9 and < 12 => "autumn",
12 or (>= 1 and < 3) => "winter",
_ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),
};
Pokud je výsledek výrazu null nebo neúspěšný převod na typ konstanty pomocí Nullable nebo rozbalení, relační vzor neodpovídá výrazu.
Další informace najdete v části relační vzory poznámky k návrhu funkcí.
Logické vzory
Počínaje jazykem C# 9,0 použijete not and kombinátory, a a or vytvoříte následující logické vzory:
Negace
notvzor, který odpovídá výrazu, pokud se vzor negace neshoduje s výrazem. Následující příklad ukazuje, jak lze pomocí operátoru negace konstantníhonullvzoru zjistit, zda výraz není null:if (input is not null) { // ... }Conjunctive
andvzor, který odpovídá výrazu, pokud oba vzory odpovídají výrazu. Následující příklad ukazuje, jak lze kombinovat relační vzorce a ověřit, zda je hodnota v určitém rozsahu:Console.WriteLine(Classify(13)); // output: High Console.WriteLine(Classify(-100)); // output: Too low Console.WriteLine(Classify(5.7)); // output: Acceptable 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", };Disjunctive
orvzor, který odpovídá výrazu, pokud jedna ze vzorů odpovídá výrazu, jak ukazuje následující příklad:Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19))); // output: winter Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9))); // output: autumn Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11))); // output: spring 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}."), };
Jak ukazuje předchozí příklad, můžete opakovaně používat vzor kombinátory ve vzoru.
andVzor kombinátorem má vyšší prioritu než or . Chcete-li explicitně zadat prioritu, použijte závorky, jak ukazuje následující příklad:
static bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');
Poznámka
Pořadí, ve kterém jsou vzory kontrolovány, není definováno. V době spuštění or lze nejprve zkontrolovat vnořené vzory a vzory na pravé straně and .
Další informace najdete v části kombinátory vzoru poznámky k návrhu funkcí.
Vzor vlastnosti
Počínaje jazykem C# 8,0 použijete vzor vlastnosti , který bude odpovídat vlastnostem nebo polím výrazu proti vnořeným vzorům, jak ukazuje následující příklad:
static bool IsConferenceDay(DateTime date) => date is { Year: 2020, Month: 5, Day: 19 or 20 or 21 };
Vzor vlastnosti odpovídá výrazu, pokud výsledek výrazu není null a každý vnořený vzor odpovídá odpovídající vlastnosti nebo poli výsledku výrazu.
Můžete také přidat kontrolu typu běhu a deklaraci proměnné do vzoru vlastnosti, jak ukazuje následující příklad:
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."),
};
Vzor vlastnosti je rekurzivní vzorek. To znamená, že můžete použít libovolný vzor jako vnořený vzor. Použijte vzorek vlastnosti pro spárování částí dat proti vnořeným vzorům, jak ukazuje následující příklad:
public record Point(int X, int Y);
public record Segment(Point Start, Point End);
static bool IsAnyEndOnXAxis(Segment segment) =>
segment is { Start: { Y: 0 } } or { End: { Y: 0 } };
Předchozí příklad používá dvě funkce, které jsou k dispozici v C# 9,0 a novější: or vzorové kombinátorem a typy záznamů.
Počínaje jazykem C# 10 můžete odkazovat na vnořené vlastnosti nebo pole v rámci vzoru vlastnosti. Například můžete Refaktorovat metodu z předchozího příkladu do následujícího ekvivalentního kódu:
static bool IsAnyEndOnXAxis(Segment segment) =>
segment is { Start.Y: 0 } or { End.Y: 0 };
Další informace najdete v části vzory vlastností poznámky k návrhu funkcí a na poznámkách k návrhu funkce pro Rozšířené vzory vlastností .
Poziční vzor
Počínaje jazykem C# 8,0 použijete poziční vzorek k dekonstrukci výsledku výrazu a k porovnání výsledných hodnot s odpovídajícími vnořenými vzorci, jak ukazuje následující příklad:
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);
}
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",
};
V předchozím příkladu typ výrazu obsahuje metodu dekonstrukce , která se používá k dekonstrukci výsledku výrazu. Můžete také porovnat výrazy typů řazené kolekce členů s pozičními vzory. Tímto způsobem můžete vyhledat více vstupů proti různým vzorům, jak ukazuje následující příklad:
static decimal GetGroupTicketPriceDiscount(int groupSize, DateTime visitDate)
=> (groupSize, visitDate.DayOfWeek) switch
{
(<= 0, _) => throw new ArgumentException("Group size must be positive."),
(_, DayOfWeek.Saturday or DayOfWeek.Sunday) => 0.0m,
(>= 5 and < 10, DayOfWeek.Monday) => 20.0m,
(>= 10, DayOfWeek.Monday) => 30.0m,
(>= 5 and < 10, _) => 12.0m,
(>= 10, _) => 15.0m,
_ => 0.0m,
};
Předchozí příklad používá relační a logické vzory, které jsou k dispozici v C# 9,0 a novější.
V rámci pozičního vzoru můžete použít názvy elementů řazené kolekce členů a Deconstruct parametry, jak ukazuje následující příklad:
var numbers = new List<int> { 1, 2, 3 };
if (SumAndCount(numbers) is (Sum: var sum, Count: > 0))
{
Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}"); // output: Sum of [1 2 3] is 6
}
static (double Sum, int Count) SumAndCount(IEnumerable<int> numbers)
{
int sum = 0;
int count = 0;
foreach (int number in numbers)
{
sum += number;
count++;
}
return (sum, count);
}
Poziční vzor můžete také roztáhnout libovolným z následujících způsobů:
Přidejte kontrolu běhového typu a deklaraci proměnné, jak ukazuje následující příklad:
public record Point2D(int X, int Y); public record Point3D(int X, int Y, int Z); static string PrintIfAllCoordinatesArePositive(object point) => point switch { Point2D (> 0, > 0) p => p.ToString(), Point3D (> 0, > 0, > 0) p => p.ToString(), _ => string.Empty, };Předchozí příklad používá poziční záznamy , které implicitně poskytují
Deconstructmetodu.Použijte vzorek vlastnosti v rámci pozičního vzoru, jak ukazuje následující příklad:
public record WeightedPoint(int X, int Y) { public double Weight { get; set; } } static bool IsInDomain(WeightedPoint point) => point is (>= 0, >= 0) { Weight: >= 0.0 };Zkombinujte dvě předchozí použití, jak ukazuje následující příklad:
if (input is WeightedPoint (> 0, > 0) { Weight: > 0.0 } p) { // .. }
Poziční vzor je rekurzivní vzorek. To znamená, že můžete použít libovolný vzor jako vnořený vzor.
Další informace najdete v části poziční vzorek poznámky k návrhu funkcí.
var vzorku
Počínaje jazykem C# 7,0 použijete var vzor , který odpovídá jakémukoli výrazu, včetně null a přiřadí jeho výsledek k nové místní proměnné, jak ukazuje následující příklad:
static bool IsAcceptable(int id, int absLimit) =>
SimulateDataFetch(id) is var results
&& results.Min() >= -absLimit
&& results.Max() <= absLimit;
static int[] SimulateDataFetch(int id)
{
var rand = new Random();
return Enumerable
.Range(start: 0, count: 5)
.Select(s => rand.Next(minValue: -10, maxValue: 11))
.ToArray();
}
varVzor je užitečný v případě, že potřebujete dočasnou proměnnou v rámci logického výrazu pro uchování výsledku mezilehlého výpočtu. Můžete také použít var vzor, pokud potřebujete provést další kontroly v when případě příkrytí switch výrazu nebo příkazu, jak ukazuje následující příklad:
public record Point(int X, int Y);
static Point Transform(Point point) => point switch
{
var (x, y) when x < y => new Point(-x, y),
var (x, y) when x > y => new Point(x, -y),
var (x, y) => new Point(x, y),
};
static void TestTransform()
{
Console.WriteLine(Transform(new Point(1, 2))); // output: Point { X = -1, Y = 2 }
Console.WriteLine(Transform(new Point(5, 2))); // output: Point { X = 5, Y = -2 }
}
V předchozím příkladu var (x, y) je vzor ekvivalentem pozičního vzoru (var x, var y) .
Ve var vzorku je typem deklarované proměnné typ doby kompilace výrazu, který se shoduje se vzorem.
Další informace naleznete v části vzorek var poznámky k návrhu funkce.
Zahodit vzor
Počínaje jazykem C# 8,0 použijete vzor zahození , který _ odpovídá jakémukoli výrazu, včetně null , jak je znázorněno v následujícím příkladu:
Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday)); // output: 5.0
Console.WriteLine(GetDiscountInPercent(null)); // output: 0.0
Console.WriteLine(GetDiscountInPercent((DayOfWeek)10)); // output: 0.0
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,
};
V předchozím příkladu je použit vzor zahození pro zpracování null a libovolné celočíselné hodnoty, které nemají odpovídající člen DayOfWeek výčtu. To zaručuje, že switch výraz v příkladu zpracovává všechny možné vstupní hodnoty. Pokud ve výrazu nepoužijete vzor zahození switch a žádná ze vzorů výrazu neodpovídá vstupu, modul runtime vyvolá výjimku. Kompilátor vygeneruje upozornění, pokud switch výraz nezpracovává všechny možné vstupní hodnoty.
Vzor zahození nemůže být vzor ve is výrazu nebo switch příkazu. V těchto případech použijte var vzor s zahozením: var _ .
Další informace najdete v části zahození vzoru poznámky k návrhu funkcí.
Vzor v závorce
Počínaje jazykem C# 9,0 můžete vložit závorky kolem libovolného vzoru. Obvykle provedete zdůraznění nebo změnu priority v logických vzorech, jak ukazuje následující příklad:
if (input is not (float or double))
{
return;
}
specifikace jazyka C#
Další informace najdete v těchto poznámkách k návrhu funkcí:
- Porovnávání vzorů pro C# 7,0
- Rekurzivní porovnávání vzorů (představené v C# 8,0)
- Změny porovnávání vzorů pro C# 9,0
- Vzory rozšířených vlastností (C# 10)