Porovnávání vzorů – výrazy isswitch a operátory andor a not ve vzorech

Výraz, příkaz switch a výraz switch použijete is k porovnání vstupního výrazu s libovolným počtem charakteristik. Jazyk 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 logických andklíčových slov , ora not.

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

Vtěchtoch

  • Vzor deklarace: Chcete-li zkontrolovat typ běhu výrazu a v případě úspěchu shody přiřaďte výsledek výrazu deklarované proměnné.
  • Model typu: Kontrola typu výrazu za běhu
  • Konstantní vzor: testovat, jestli se výsledek výrazu rovná zadané konstantě.
  • Relační vzory: porovnání výsledku výrazu se zadanou konstantou
  • Logické vzory: k otestování, jestli výraz odpovídá logické kombinaci vzorů.
  • Vzor vlastnosti: Testování, jestli vlastnosti výrazu nebo pole odpovídají vnořeným vzorům.
  • Poziční vzor: dekonstrukce výsledku výrazu a testování, jestli výsledné hodnoty odpovídají vnořeným vzorům.
  • var pattern: aby odpovídal libovolnému výrazu a přiřadil jeho výsledek deklarované proměnné.
  • Zahodit vzor: aby se shodovaly s libovolným výrazem.
  • Vzory seznamu: k otestování, jestli se sekvencí elementy shodují s odpovídajícími vnořenými vzory. Představeno v C# 11.

Logické, vlastnosti, poziční vzory a vzory seznamů jsou rekurzivní vzory. To znamená, že mohou obsahovat vnořené vzory.

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

Vzory deklarací a typů

Pomocí deklarací a vzorů typů můžete zkontrolovat, jestli je typ běhu 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 tato proměnná přiřazena převedený výsledek 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 je výsledek výrazu nenulový 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 je odvozen od typu T, implementuje rozhraní Tnebo jiný implicitní převod odkazu existuje z něj .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 při prvním volání GetSourceLabel metody první vzor odpovídá hodnotě argumentu, protože typ int[] běhu argumentu Array je odvozen od typu. Při druhém volání GetSourceLabel metody typ runtime List<T> argumentu není odvozen od Array typu, ale implementuje ICollection<T> rozhraní.

  • Typ běhu výsledku výrazu je typ hodnoty null s podkladovým typem T.

  • Převod boxingu nebo rozbalení existuje z typu běhu výsledku výrazu, který se má zadat 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)),
    };
}

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 odpovídá vzor typu výrazu, pokud je výsledek výrazu nenulový a jeho typ běhu splňuje některou z výše uvedených podmínek.

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

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

Další informace najdete v částech Vzor deklarace a Vzor typů v poznámkách k návrhu funkcí.

Konstantní vzor

Pomocí konstantního vzoru otestujete, 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ý se konvertibilní na typ konstanty, s jednou výjimkou: Výraz, jehož typ je Span<char> nebo ReadOnlySpan<char> lze shodovat s řetězci konstant v jazyce 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.

Ke kontrole nenulové konstanty můžete použít negovanýnull vzor, jak ukazuje následující příklad:

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

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

Relační vzory

K porovnání výsledku výrazu s konstantou použijete relační vzor , 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. Konstantní výraz může být celé číslo, číslo s plovoucí desetinou čárkou, znak nebo typ výčtu.

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

Další informace naleznete v části Relační vzory návrhu funkce poznámka.

Logické vzory

Pomocí notkombinátorů , anda or vzorů vytvoříte následující logické vzory:

  • Vzor negacenot , který odpovídá výrazu, pokud negovaný vzor neodpovídá výrazu. Následující příklad ukazuje, jak můžete negovat vzor konstantynull , abyste zkontrolovali, jestli je výraz nenulový:

    if (input is not null)
    {
        // ...
    }
    
  • Vzor konjunktivyand , který odpovídá výrazu, když 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é oblasti:

    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",
    };
    
  • Disjunktivníor 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žívat kombinátory vzorů ve vzoru.

Priorita a pořadí kontroly

Kombinátory vzorů jsou seřazené od nejvyšší priority k nejnižšímu následujícímu:

  • not
  • and
  • or

Pokud je logickým vzorem is výrazu, priorita kombinátorů logických vzorů je vyšší než priorita logických operátorů ( bitové logické i logické operátory logické). Jinak je priorita kombinátorů logického vzoru nižší než priorita logických a podmíněných logických operátorů. Úplný seznam operátorů jazyka C# seřazených podle úrovně priority najdete v části Priorita operátorů v článku operátory jazyka C#.

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 jsou vzorky kontrolovány, není definováno. V době běhu je možné nejprve zkontrolovat vnořené vzory a and vzory or zprava.

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

Vzor vlastností

Vzor vlastnosti použijete 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 je výsledek výrazu nenulový 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 za 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í vzor. To znamená, že jako vnořený vzor můžete použít libovolný vzor. Pomocí vzoru vlastnosti můžete shodovat čá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á kombinátor vzorůor a typy záznamů.

Počínaje jazykem C# 10 můžete odkazovat na vnořené vlastnosti nebo pole v rámci vzoru vlastnosti. Tato funkce se označuje jako vzor 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 naleznete v části Vzor vlastnosti návrhu funkce poznámky a rozšířené vzory vlastností návrhu návrhu.

Tip

Pomocí pravidla stylu Zjednodušit (IDE0170) můžete zlepšit čitelnost kódu navržením míst pro použití rozšířených vzorů vlastností.

Poziční vzor

K dekonstrukci výsledku výrazu použijete poziční vzor a porovnáte výsledné hodnoty 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á slouží k dekonstrukci výsledku výrazu. Můžete také spárovat výrazy typů řazené kolekce členů proti pozičním vzorům. Tímto způsobem můžete spárovat 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.

Názvy elementů řazené kolekce členů a Deconstruct parametrů 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 rozšířit také některým z následujících způsobů:

  • Přidejte kontrolu typu za 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 naleznete v části Poziční vzor návrhu návrhu.

var Vzor

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

Vzor var je užitečný v případě, že potřebujete dočasnou proměnnou v rámci logického výrazu k uložení výsledku průběžných výpočtů. Vzor můžete použít var také v případě, že potřebujete provádět další kontroly v when případě switch , že chrání výraz nebo příkaz, 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 V modelu je typem deklarované proměnné typ kompilace výrazu, který se shoduje se vzorem.

Další informace naleznete v části Var vzor návrhu funkce poznámka.

Zahodit vzor

Vzor zahození_ použijete ke shodě libovolného výrazu, včetně null, jak ukazuje následující příklad:

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 vzor zahození používá ke zpracování null a jakékoli celočíselné hodnotě, která nemá odpovídající člen výčtu DayOfWeek . To zaručuje, že switch výraz v příkladu zpracovává všechny možné vstupní hodnoty. Pokud ve výrazu switch 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 is výrazu switch ani příkazu. V takových případech použijte vzor var s zahozením: . var _ Vzor zahození může být vzorem ve výrazu switch .

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

Vzor závorek

Závorky můžete umístit kolem libovolného vzoru. Obvykle to uděláte tak, že zvýrazníte nebo změníte prioritu v logických vzorech, jak ukazuje následující příklad:

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

Vzory seznamů

Počínaje jazykem C# 11 můžete spárovat pole nebo seznam s posloupností 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, porovnává se vzor seznamu, když se každý vnořený vzor shoduje s odpovídajícím prvkem vstupní sekvence. V rámci vzoru seznamu můžete použít libovolný vzor. Pokud chcete shodovat s jakýmkoli prvkem , 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 shodovat 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ů. V vzoru seznamu můžete použít maximálně jeden vzor řezu. Vzor řezu se může zobrazit jenom v vzoru seznamu.

Podpattern 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í seznamu vzorů .

specifikace jazyka C#

Další informace najdete v části Vzory a porovnávání vzorů specifikace jazyka C#.

Informace o funkcích přidaných v jazyce C# 8 a novějších najdete v následujících poznámkách k návrhu funkcí:

Viz také