Omówienie dopasowywania wzorca

Porada

Ten artykuł jest częścią sekcji Podstawy dla deweloperów, którzy już znają co najmniej jeden język programowania i uczą się języka C#. Jeśli dopiero zaczynasz programować, najpierw zacznij od samouczków Wprowadzenie . Doświadczeni deweloperzy języka C# mogą używać odwołań do wzorców dla pełnej składni i reguł.

Pochodzi z innego języka? Wzorce języka C# pełnią rolę podobną do dopasowywania wzorców Java i przypadków Pythonmatch. Język C# używa wzorców w is wyrażeniach, switch instrukcjach i switch wyrażeniach.

Dopasowywanie wzorca stosuje wzorzec do wyrażenia. Wzorzec to warunek testowania wyrażenia wejściowego. Dane wejściowe wzorca to to wyrażenie. Język C# oblicza wyrażenie wejściowe; wynik jest obliczoną wartością. Wzorzec sprawdza, czy ta wartość ma określony typ, jest równa określonej wartości, czy ma określony kształt. Gdy wzorzec jest zgodny, kod może używać informacji poznanych przez test, takich jak bardziej konkretny typ lub wartość wyodrębniona z obiektu.

Można użyć wzorca w trzech kontekstach:

  • Po prawej stronie is operatora.
  • W etykiecie instrukcji caseswitch.
  • W ramieniu wyrażenia switch.

Wzorce są często bardziej przejrzyste niż ciąg instrukcji porównujących, ponieważ każda gałąź opisuje dane, które obsługuje. Na przykład poniższa metoda używa wyrażenia switch, aby wybrać komunikat o dostawie:

static string GetDeliveryMessage(Delivery? delivery) =>
    delivery switch
    {
        null => "No delivery was scheduled.",
        ExpressDelivery express => $"Express package {express.TrackingCode}",
        StandardDelivery { Days: <= 2 } => "Standard delivery arriving soon",
        _ => "Standard delivery"
    };

Wzorce cykliczne mają własne wyrażenia wejściowe. W StandardDelivery { Days: <= 2 } wzorzec zewnętrzny otrzymuje wyrażenie delivery. Wzorzec rekurencyjny <= 2 otrzymuje wyrażenie właściwości Days z dopasowanego obiektu StandardDelivery.

Wyrażenie przed switch jest wyrażeniem wejściowym. Każda linia wewnątrz nawiasów klamrowych jest ramieniem przełącznika. Wzorzec zostanie wyświetlony przed elementem =>, a wynik pojawi się po nim. Język C# oblicza wartość wyrażenia wejściowego, a następnie wybiera pierwsze ramię, zgodnie z kolejnością w tekście, którego wzorzec jest zgodny i którego opcjonalny warunek when ma wartość true. Opcjonalny warunek ochronny when jest dodatkowym warunkiem boolowskim zapisanym po wzorcu. W poprzednim przykładzie pokazano następujące wzorce:

  • null jest stałym wzorcem. Sprawdza, czy delivery wyrażenie daje wartość null.
  • ExpressDelivery express jest wzorcem deklaracji z dwiema częściami. ExpressDelivery to część badania typu. Sprawdza, czy wartość oceniana jest obiektem niebędącym wartością null, którego typ czasu wykonywania jest zgodny z elementem ExpressDelivery. express to oznaczenie zmiennej: deklaruje zmienną o nazwie express i przypisuje do niej dopasowany ExpressDelivery obiekt.
  • StandardDelivery { Days: <= 2 } rozpoczyna się od testu typu. StandardDelivery sprawdza, czy wartość oceniana jest obiektem o wartości innej niż null tego typu. Nawiasy klamrowe zawierają wzorzec właściwości. Days określa nazwę właściwości do sprawdzenia, więc wyrażenie właściwości Days staje się elementem wejściowym dla wzorca rekurencyjnego. Część <= 2 jest wzorcem relacyjnym, który sprawdza, czy obliczona wartość jest mniejsza, czy równa 2.
  • _ (podkreślenie) to wzorzec odrzucania. Pasuje do każdej obliczonej wartości, w tym null. Ponieważ wcześniejsze ramiona już obsługują null, dostawy ekspresowe oraz standardowe dostawy docierające w ciągu dwóch dni, to ostatnie ramię obsługuje każdą pozostałą wartość.

Ramię bez osłony when jest niestrzeżone. Wszystkie ramiona w pierwszym przykładzie są niestrzeżone. Jeśli wcześniej niestrzeżone ramię pasuje do każdej obliczonej wartości, którą może dopasować późniejsza ramię, późniejsze ramię jest podsumowywane. Wzorzec podsumowany jest taki, w którym każda możliwa wartość wejściowa zgodna została już dopasowana przez wcześniejsze ramię przełącznika. Nigdy nie może być zgodna, więc kompilator zgłasza błąd. Gałąź odrzucająca (_) musi być ostatnia, ponieważ dopasowuje się do każdego wyrażenia wejściowego. Chronione ramię nie subsumuje późniejszego ramienia na podstawie samego wzorca, ponieważ strażnik może być false.

Wyrażenie przełącznika jest wyczerpujące , gdy jego ramiona obsługują każde możliwe wyrażenie wejściowe. Pierwszy przykład jest pełny, ponieważ jego ostatnia gałąź odrzucająca obsługuje wszystko, czego nie dopasują wcześniejsze gałęzie. Kompilator ostrzega, gdy wykrywa potencjalną wartość wejściową, której nie obsługuje żadna gałąź. Kompilator nie może udowodnić wyczerpującości dla każdej kombinacji wzorców, ale ta diagnostyka ułatwia pisanie poprawnego kodu pasującego do wzorca. Aby uzyskać szczegółowe informacje na temat dopasowywania, podsumpcji i reguł wyczerpującości, zobacz dokumentację wzorców.

Testowanie jednego warunku za pomocą polecenia is

is Użyj operatora, jeśli potrzebujesz jednego testu logicznego. Poniższy kod oblicza wartość wyrażenia delivery i stosuje wzorzec deklaracji ExpressDelivery express. Część typu pasuje, gdy obliczana wartość jest różna od null, a jej typ w czasie wykonywania jest zgodny z ExpressDelivery. Gdy wzorzec jest zgodny, jego oznaczenie zmiennej deklaruje :express

static void PrintTrackingCode(Delivery delivery)
{
    if (delivery is ExpressDelivery express)
    {
        Console.WriteLine($"Track express package {express.TrackingCode}");
    }
}

Kompilator śledzi, czy do zmiennej lokalnej przypisano wartość, zanim kod odczyta jej wartość. To śledzenie jest nazywane określonym przypisaniem. Wewnątrz bloku if warunek może mieć wartość true tylko wtedy, gdy wzorzec przypisał pasujący obiekt do express. W związku z tym kompilator wie, że express jest tam zdecydowanie przypisany. Kod może bezpiecznie używać jego TrackingCode właściwości. Nie potrzebujesz oddzielnej obsady. Jeśli potrzebujesz tylko testu typu i nie musisz deklarować zmiennej, użyj wzorca typu, takiego jak delivery is ExpressDelivery.

Użyj polecenia is null lub is not null w przypadku sprawdzania wartości null. Te wzorce nie wywołują operatora == ani != zdefiniowanego przez użytkownika.

static void PrintPackageStatus(Delivery? delivery)
{
    if (delivery is null)
    {
        Console.WriteLine("No package is available.");
    }
    else
    {
        Console.WriteLine("A package is ready to track.");
    }
}

Aby uzyskać więcej wskazówek dotyczących bezpiecznej obsługi wartości null, zobacz typy referencyjne dopuszczające wartość null.

Wybieranie między instrukcją a wyrażeniem

Wzorce działają z obiema formami switch:

  • Użyj instrukcjiswitch , gdy każde dopasowanie powinno uruchamiać co najmniej jedną instrukcję.
  • switch Użyj wyrażenia, gdy każde dopasowanie powinno wygenerować wartość.

Poniższa instrukcja switch informuje o aktualizacji dostawy. Gałąź express-delivery zapisuje dwa komunikaty, więc instrukcja pasuje naturalnie:

static void PrintDeliveryUpdate(Delivery? delivery)
{
    switch (delivery)
    {
        case null:
            Console.WriteLine("No delivery was scheduled.");
            break;
        case ExpressDelivery express:
            Console.WriteLine($"Express delivery {express.TrackingCode} is ready.");
            Console.WriteLine("Notify the priority desk.");
            break;
        case StandardDelivery standard:
            Console.WriteLine($"Standard delivery arrives in {standard.Days} days.");
            break;
        default:
            Console.WriteLine("Another delivery type is scheduled.");
            break;
    }
}

Każde case stosuje wzorzec do wyrażenia delivery. Sekcja dopasowania może uruchamiać dowolną liczbę instrukcji przed break zamknięciem przełącznika. Sekcja default obsługuje wszystkie elementy, które nie są zgodne z wcześniejszymi przypadkami.

Następujące wyrażenie przełącznika if / else if zastępuje łańcuch, który przypisuje jeden wynik:

static string GetStatusMessage(StandardDelivery delivery) =>
    delivery.Days switch
    {
        0 => "Delivered today",
        1 => "Arriving tomorrow",
        <= 3 => "Arriving soon",
        _ => "In transit"
    };

Wyrażenie przełącznika jest zwięzłe, ponieważ każde ramię ma ten sam cel: generuj zwróconą wartość. Użyj instrukcji switch, gdy gałęzie wykonują operacje, a wyrażenia switch, gdy gałęzie obliczają jeden wynik.

Kategorie wzorów

Język C# zawiera wzorce typowych rodzajów testów danych:

Kategoria wzorca Co testuje
Deklaracja, stała i var wzorce Typ czasu wykonywania, określona wartość stała lub dowolna wartość, którą chcesz przechwycić
Wzorce typów Typ czasu wykonywania bez deklarowania zmiennej
Wzorce właściwości i wzorce pozycyjne Właściwości, pola lub dekonstrukturowane wartości
Wzorce relacyjne i logiczne Porównania i kombinacje, takie jak and, ori not
Wzorce listy Wartości i kształt listy lub tablicy
Odrzuć wzorce i odrzucenia Dowolna pozostała wartość lub wartość, która kod celowo ignoruje

Artykuły z serii Podstawy, do których prowadzą linki w tabeli, zawierają szczegółowe omówienie kategorii obecnie opisanych w tej sekcji. Aby uzyskać pełną składnię i przykłady dla wszystkich kategorii wzorców, zobacz dokumentację wzorców.

Zobacz też