Porovnávání vzorů is – výrazy a switch operátory anda ve notor vzorech

Výraz, příkaz switch a výraz switch použijete isk porovnání vstupního výrazu s libovolným počtem charakteristik. C# podporuje více vzorů, včetně deklarace, typu, konstanty, relačního, vlastnosti, seznamu, var a zahození. Vzory lze kombinovat pomocí logických logických andklíčových slov , ora not.

Následující výrazy a příkazy jazyka C# podporují porovnávání vzorů:

V těchto konstruktorech můžete spárovat vstupní výraz s některým z následujících vzorů:

  • Vzor deklarace: Chcete-li zkontrolovat typ výrazu za běhu, a pokud je shoda úspěšná, přiřaďte výsledek výrazu deklarované proměnné.
  • Typový vzor: Zkontrolujete typ výrazu za běhu. Zavedeno v jazyce C# 9.0.
  • Konstantní vzor: Testuje, jestli se výsledek výrazu rovná zadané konstantě.
  • Relační vzory: k porovnání výsledku výrazu se zadanou konstantou. Zavedeno v jazyce C# 9.0.
  • Logické vzory: Testuje, jestli výraz odpovídá logické kombinaci vzorů. Zavedeno v jazyce C# 9.0.
  • Vzor vlastností: k otestování, jestli vlastnosti nebo pole výrazu odpovídají vnořeným vzorům.
  • Poziční vzor: dekonstruuje výsledek výrazu a otestuje, jestli se výsledné hodnoty shodují s vnořenými vzory.
  • var pattern( vzor): pro spárování libovolného výrazu a přiřazení jeho výsledku k deklarované proměnné.
  • Zahodit vzor: aby se shodovaly s libovolným výrazem.
  • Vzory seznamu: Testuje, jestli se sekvenční prvky shodují s odpovídajícími vnořenými vzory. Zavedeno v C# 11.

Logické vzory, vzory vlastností, umístění a seznamů jsou rekurzivní vzory. To znamená, že můžou obsahovat vnořené vzory.

Příklad použití těchto vzorů k vytvoření algoritmu řízeného daty najdete v tématu Kurz: Použití porovnávání vzorů k sestavení algoritmů řízených typem a daty.

Vzory deklarací a typů

Pomocí vzorů deklarací a typů můžete zkontrolovat, jestli je typ výrazu za běhu 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!
}

Vzor deklarace s typem T odpovídá výrazu, pokud výsledek výrazu není null a platí některé z následujících podmínek:

  • Typ běhu výsledku výrazu je T.

  • Typ běhu výsledku výrazu se odvozuje od typu T, implementuje rozhraní Tnebo z něj existuje jiný implicitní převod odkazu na T. Následující příklad ukazuje dva případy, kdy 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 se při prvním volání GetSourceLabel metody první vzor shoduje s hodnotou argumentu, protože typ int[] běhu argumentu je odvozen od Array typu. Při druhém volání GetSourceLabel metody se typ List<T> běhu argumentu neodvozuje z Array typu, ale implementuje ICollection<T> rozhraní.

  • Typ výsledku výrazu za běhu je typ hodnoty s možnou hodnotou null s podkladovým typem T.

  • Existuje převod boxingu nebo unboxingu z typu run-time výsledku výrazu na typ T.

Následující příklad ukazuje 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
}

Pokud chcete zkontrolovat pouze typ výrazu, můžete místo názvu proměnné použít zahození _ , 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 můžete k tomuto účelu použít typový vzor, 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 odpovídá vzor typu výrazu, pokud výsledek výrazu není null a jeho typ za běhu splňuje některou z výše uvedených podmínek.

Pokud chcete zkontrolovat nenulovou hodnotu, můžete použít vzor negovanénullkonstanty, jak ukazuje následující příklad:

if (input is not null)
{
    // ...
}

Další informace najdete v částech Vzor deklarace a Typ v poznámkách k návrhu funkce.

Konstantní vzor

Pomocí konstantního vzoru můžete otestovat, jestli se výsledek výrazu rovná 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:

Výraz musí být typ, který je převoditelný na konstantní typ, s jednou výjimkou: Výraz, jehož typ je Span<char> nebo ReadOnlySpan<char> může být spárován s konstantními řetězci v C# 11 a novějších verzích.

Pomocí konstantního vzoru zkontrolujte null, jak ukazuje následující příklad:

if (input is null)
{
    return;
}

Kompilátor zaručuje, že se při vyhodnocení výrazu x is null nevyvolá žádný operátor == rovnosti přetížený uživatelem.

Počínaje jazykem C# 9.0 můžete pomocí negovanéhonull vzoru konstanty zkontrolovat, jestli není null, jak ukazuje následující příklad:

if (input is not null)
{
    // ...
}

Další informace najdete v části Konstantní vzor poznámky k návrhu funkce.

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 libovolný z relačních operátorů< , ><=nebo >=. Pravá část relačního vzoru musí být konstantní výraz. Výraz konstanty může být typu integer, floating-point, char nebo enum .

Pokud chcete zkontrolovat, jestli je výsledek výrazu v určitém rozsahu, porovnáte ho se vzorem spojivekand, 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 výsledek výrazu je null nebo se nepodaří převést na typ konstanty převodem s možnou hodnotou null nebo unboxing, relační vzor neodpovídá výrazu.

Další informace najdete v části Relační vzory v poznámce k návrhu funkce.

Logické vzory

Počínaje jazykem C# 9.0 vytvoříte notpomocí kombinátorů , anda or vzorů následující logické vzory:

  • Negacenot vzor, který odpovídá výrazu, když negovaný vzor neodpovídá výrazu. Následující příklad ukazuje, jak můžete negovat konstantnínull vzor a zkontrolovat, jestli výraz není null:

    if (input is not null)
    {
        // ...
    }
    
  • Konjunktivand vzor, který odpovídá výrazu, pokud oba vzory odpovídají výrazu. Následující příklad ukazuje, jak můžete zkombinovat relační vzory a zkontrolovat, jestli 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",
    };
    
  • Disjunktivor vzor, který odpovídá výrazu, když některý 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žít kombinátory vzorů ve vzoru.

Priorita a pořadí kontroly

Následující seznam uvádí kombinátory vzorů od nejvyšší priority po nejnižší:

  • not
  • and
  • or

Pokud chcete explicitně určit 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 se vzory kontrolují, není definováno. V době běhu je možné nejprve zkontrolovat vnořené vzory or a and vzory zprava.

Další informace najdete v části Kombinátory vzorů v poznámce k návrhu funkce.

Vzor vlastnosti

Vzor vlastností můžete použít ke spárování vlastností výrazu nebo polí s vnořenými vzory, 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.

Do vzoru vlastnosti můžete také přidat kontrolu typu za běhu a deklaraci proměnné, 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í vzor. To znamená, že jako vnořený vzor můžete použít libovolný vzor. Pomocí vzoru vlastnosti můžete spárovat části dat s vnořenými vzory, 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 dostupné v jazyce C# 9.0 a novější: orkombinátor vzorů a typy záznamů.

Počínaje jazykem C# 10 můžete odkazovat na vnořené vlastnosti nebo pole v rámci vzoru vlastností. Tato funkce se označuje jako model rozšířených vlastností. Můžete například 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 Model vlastností v poznámce k návrhu funkce a v poznámce k návrhu funkce rozšířených vzorů vlastností .

Tip

Můžete použít pravidlo stylu Model Zjednodušit (IDE0170) ke zlepšení čitelnosti kódu tím, že navrhnete místa pro použití rozšířených vzorů vlastností.

Poziční vzor

Poziční vzor slouží k dekonstrukci výsledku výrazu a porovnávání výsledných hodnot s odpovídajícími vnořenými vzory, 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 Deconstruct metoda, 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 porovnat více vstupů s různými vzory, 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 jazyce C# 9.0 a novějších.

Názvy prvků a Deconstruct parametrů řazené kolekce členů můžete použít v pozičním vzoru, 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é rozšířit některým z následujících způsobů:

  • Přidejte kontrolu typu běhu 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í metodu Deconstruct .

  • Použijte vzor 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í vzor. To znamená, že jako vnořený vzor můžete použít libovolný vzor.

Další informace najdete v části Positional pattern (Poziční vzor ) poznámky k návrhu funkce.

var Vzor

Vzor použijete ke spárování libovolného var výrazu, včetně null, a jeho výsledek přiřadíte 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();
}

Vzor var je užitečný, když potřebujete dočasnou proměnnou v rámci logického výrazu, která bude obsahovat výsledek průběžných výpočtů. Vzor můžete použít var také v případě, že potřebujete provést více kontrol v when případě ochrany výrazu switch 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 je vzor var (x, y) ekvivalentní pozičnímu vzoru(var x, var y).

var Ve vzoru je typem deklarované proměnné typ kompilace výrazu, který se shoduje se vzorem.

Další informace najdete v části Vzor var poznámky k návrhu funkce.

Zahodit vzor

Vzor zahození_ použijete, aby odpovídal libovolnému 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 se ke zpracování null a jakékoli celočíselné hodnoty, která nemá odpovídající člen výčtu DayOfWeek , používá vzor zahození. To zaručuje, že switch výraz v příkladu zpracuje všechny možné vstupní hodnoty. Pokud ve switch výrazu nepoužíváte vzor zahození a žádný ze vzorů výrazu neodpovídá vstupu, modul runtime vyvolá výjimku. Kompilátor vygeneruje upozornění, pokud switch výraz nezpracuje všechny možné vstupní hodnoty.

Vzor zahození nemůže být vzorem ve výrazu isswitch ani příkazu. V těchto případech použijte vzor se zahozením, který odpovídá libovolnému výrazuvar: var _.

Další informace najdete v části Zahodit vzor poznámky k návrhu funkce.

Vzor v závorkách

Počínaje jazykem C# 9.0 můžete vložit závorky kolem libovolného vzoru. Obvykle to uděláte tak, že zvýrazníte nebo změníte prioritu v logických vzorcích, jak ukazuje následující příklad:

if (input is not (float or double))
{
    return;
}

Vzory seznamů

Počínaje C# 11 můžete porovnat pole nebo seznam se sekvencí vzorů, jak ukazuje následující příklad:

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]);  // True

Jak ukazuje předchozí příklad, vzor seznamu je spárován, když je každý vnořený vzor spárován odpovídajícím prvkem vstupní sekvence. V rámci vzoru seznamu můžete použít libovolný vzor. Chcete-li porovnat libovolný prvek, použijte vzor zahození nebo, pokud chcete také zachytit element, vzor var, jak ukazuje následující příklad:

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

if (numbers is [var first, _, _])
{
    Console.WriteLine($"The first element of a three-item list is {first}.");
}
// Output:
// The first element of a three-item list is 1.

Předchozí příklady odpovídají celé vstupní sekvenci se vzorem seznamu. Pokud chcete porovnat prvky pouze na začátku nebo/a na konci vstupní sekvence, použijte vzor ..řezu, jak ukazuje následující příklad:

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

Vzor řezu odpovídá nule nebo více prvků. Ve vzoru seznamu můžete použít maximálně jeden vzor řezu. Vzorek řezu se může zobrazit jenom ve vzoru seznamu.

Dílčípattern můžete také vnořit do vzoru řezu, jak ukazuje následující příklad:

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);
}

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

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

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

Další informace najdete v poznámce k návrhu funkcí seznamů vzorů .

specifikace jazyka C#

Další informace najdete v následujících poznámkách k návrhu funkce:

Viz také