C # 7 hat endlich eine lang erwartete Funktion namens Pattern Matching. Wenn Sie mit funktionalen Sprachen wie F # vertraut sind, kann Sie diese Funktion in der Form, in der sie derzeit vorhanden ist, leicht enttäuschen. Aber auch heute kann es den Code in einer Vielzahl von Fällen vereinfachen. Weitere Details unter dem Schnitt!

Jede neue Funktion kann für den Entwickler gefährlich sein, wenn er die Anwendung erstellt, für die die Leistung entscheidend ist. Neue Abstraktionsebenen sind gut, aber um sie effektiv zu nutzen, müssen Sie verstehen, wie sie tatsächlich funktionieren. Dieser Artikel beschreibt die Mustervergleichsfunktion und ihre Funktionsweise.
Ein Beispiel in C # kann sowohl in einem is-Ausdruck als auch im case-Block einer switch-Anweisung verwendet werden.
Es gibt drei Arten von Proben:
- Probenkonstante;
- Typ Probe;
- Beispielvariable.
Musterabgleich in ist Ausdrücke
public void IsExpressions(object o) {
Mit dem Ausdruck is können Sie überprüfen, ob der Wert konstant ist, und mit der Typprüfung können Sie zusätzlich die Beispielvariable bestimmen.
Wenn Sie den Mustervergleich in is-Ausdrücken verwenden, sollten Sie einige interessante Punkte beachten:
- Die von der if-Anweisung eingegebene Variable wird an den äußeren Bereich gesendet.
- Die von der if-Anweisung eingegebene Variable wird nur explizit zugewiesen, wenn das Muster übereinstimmt.
- Die derzeitige Implementierung des Mustervergleichs in Ausdrücken ist nicht sehr effizient.
Betrachten Sie zunächst die ersten beiden Fälle:
public void ScopeAndDefiniteAssigning(object o) { if (o is string s && s.Length != 0) { Console.WriteLine("o is not empty string"); }
Die erste if-Anweisung führt die Variable s ein, die in der gesamten Methode sichtbar ist. Dies ist sinnvoll, verkompliziert jedoch die Logik, wenn andere if-Anweisungen im selben Block versuchen, denselben Namen wiederzuverwenden. Verwenden Sie in diesem Fall einen anderen Namen, um Konflikte zu vermeiden.
Die im Ausdruck is eingegebene Variable wird nur dann explizit zugewiesen, wenn das Prädikat wahr ist. Dies bedeutet, dass die Variable n in der zweiten if-Anweisung nicht im richtigen Operanden zugewiesen ist. Da sie jedoch bereits deklariert ist, können wir sie als out-Variable in der int.TryParse-Methode verwenden.
Der dritte oben erwähnte Punkt ist der wichtigste. Betrachten Sie das folgende Beispiel:
public void BoxTwice(int n) { if (n is 42) Console.WriteLine("n is 42"); }
In den meisten Fällen wird der Ausdruck in object.Equals (Konstante, Variable) konvertiert [obwohl die Merkmale besagen, dass der Operator == für einfache Typen verwendet werden sollte]:
public void BoxTwice(int n) { if (object.Equals(42, n)) { Console.WriteLine("n is 42"); } }
Dieser Code ruft zwei Paketkonvertierungsprozesse auf, die die Leistung erheblich beeinträchtigen können, wenn sie in einem kritischen Anwendungspfad verwendet werden. Früher war der Ausdruck o ein Null-Ausdruck, der als Verpackung bezeichnet wurde, wenn die Variable o vom Typ nullable war (siehe
Suboptimaler Code für e ist null ), aber es besteht die Hoffnung, dass dies behoben wird (hier ist die entsprechende
Anforderung auf Github ).
Wenn die Variable n vom Typ Objekt ist, verursacht der Ausdruck o is 42 einen "Packing-Conversion" -Prozess (für Literal 42), obwohl ein ähnlicher Code, der auf der switch-Anweisung basiert, nicht dazu führen würde.
Beispielvariable in ist Ausdruck
Ein variables Muster ist eine spezielle Art von Mustertyp mit einem großen Unterschied: Das Muster stimmt mit jedem Wert überein, sogar mit Null.
public void IsVar(object o) { if (o is var x) Console.WriteLine($"x: {x}"); }
Der Ausdruck o is object ist wahr, wenn o nicht null ist, aber der Ausdruck o is var x ist immer wahr. Daher schließt der Compiler im Release-Modus * if-Anweisungen vollständig aus und verlässt einfach den Console-Methodenaufruf. Leider warnt der Compiler im folgenden Fall nicht vor der Nichtverfügbarkeit des Codes: if (! (O ist var x)) Console.WriteLine ("Unreachable"). Es besteht die Hoffnung, dass dies auch behoben wird.
* Es ist unklar, warum sich das Verhalten nur im Release-Modus unterscheidet. Es scheint, dass die Wurzel aller Probleme dieselbe ist: Die anfängliche Implementierung der Funktion ist nicht optimal. Nach diesem Kommentar von Neal Gafter wird sich jedoch bald alles ändern: „Der Code für den Abgleich mit dem Beispiel wird von Grund auf neu geschrieben (um auch rekursive Beispiele zu unterstützen). Ich denke, die meisten Verbesserungen, über die Sie sprechen, werden im neuen Code implementiert und sind kostenlos verfügbar. Dies wird jedoch einige Zeit dauern. “Das Fehlen einer Nullprüfung macht diese Situation besonders und potenziell gefährlich. Wenn Sie jedoch genau wissen, wie dieses Beispiel funktioniert, kann es hilfreich sein. Es kann verwendet werden, um eine temporäre Variable in den Ausdruck einzufügen:
public void VarPattern(IEnumerable<string> s) { if (s.FirstOrDefault(o => o != null) is var v && int.TryParse(v, out var n)) { Console.WriteLine(n); } }
Ist Ausdruck und Elvis Aussage
Es gibt einen anderen Fall, der sich als nützlich erweisen kann. Ein Beispieltyp stimmt nur dann mit einem Wert überein, wenn er nicht null ist. Wir können diese "Filter" -Logik mit einem nullverteilten Operator verwenden, um den Code besser lesbar zu machen:
public void WithNullPropagation(IEnumerable<string> s) { if (s?.FirstOrDefault(str => str.Length > 10)?.Length is int length) { Console.WriteLine(length); }
Beachten Sie, dass dasselbe Muster sowohl für Werttypen als auch für Referenztypen verwendet werden kann.
Mustervergleich in Fallblöcken
Die Funktionalität der switch-Anweisung wurde in C # 7 erweitert, sodass jetzt Muster in case-Klauseln verwendet werden können:
public static int Count<T>(this IEnumerable<T> e) { switch (e) { case ICollection<T> c: return c.Count; case IReadOnlyCollection<T> c: return c.Count;
Dieses Beispiel zeigt die ersten Änderungen an der switch-Anweisung.
- Mit der switch-Anweisung kann eine Variable eines beliebigen Typs verwendet werden.
- Mit der case-Klausel können Sie ein Muster angeben.
- Die Reihenfolge der Fallklauseln ist wichtig. Der Compiler gibt einen Fehler aus, wenn der vorherige Satz dem Basistyp und der nächste dem abgeleiteten Satz entspricht.
- Benutzerdefinierte Angebote werden implizit auf null ** geprüft. Im obigen Beispiel ist die letzte case-Klausel gültig, da sie nur übereinstimmt, wenn das Argument nicht null ist.
** Der letzte Satz des Falls zeigt eine weitere in C # 7 hinzugefügte Funktion - Beispiele einer leeren Variablen. Der spezielle Name _ teilt dem Compiler mit, dass die Variable nicht benötigt wird. Das Typbeispiel in der case-Klausel erfordert einen Alias. Aber wenn Sie es nicht brauchen, können Sie _ verwenden.Das folgende Snippet zeigt eine weitere Funktion des Mustervergleichs basierend auf der switch-Anweisung - die Möglichkeit, Prädikate zu verwenden:
public static void FizzBuzz(object o) { switch (o) { case string s when s.Contains("Fizz") || s.Contains("Buzz"): Console.WriteLine(s); break; case int n when n % 5 == 0 && n % 3 == 0: Console.WriteLine("FizzBuzz"); break; case int n when n % 5 == 0: Console.WriteLine("Fizz"); break; case int n when n % 3 == 0: Console.WriteLine("Buzz"); break; case int n: Console.WriteLine(n); break; } }
Dies ist eine seltsame Version der
FizzBuzz- Aufgabe, die ein Objekt verarbeitet, nicht nur eine Zahl.
Eine switch-Anweisung kann mehrere case-Klauseln desselben Typs enthalten. In diesem Fall kombiniert der Compiler alle Typprüfungen, um unnötige Berechnungen zu vermeiden:
public static void FizzBuzz(object o) {
Es sind jedoch zwei Dinge zu beachten:
1. Der Compiler kombiniert nur sequentielle Typprüfungen. Wenn Sie Case-Klauseln mit verschiedenen Typen mischen, wird ein Code mit geringerer Qualität generiert:
switch (o) {
Der Compiler konvertiert es wie folgt:
if (o ist int n && n == 1) return 1;
if (o is string s && s == "") return 2; if (o is int n2 && n2 == 2) return 3; return -1;
2. Der Compiler unternimmt alles, um typische Sequenzierungsprobleme zu vermeiden.
switch (o) { case int n: return 1;
Der Compiler kann jedoch nicht feststellen, dass ein Prädikat stärker als ein anderes ist, und ersetzt effektiv die folgenden case-Klauseln:
switch (o) { case int n when n > 0: return 1;
Muster-Matching-Brief
- Die folgenden Muster wurden in C # 7 angezeigt: ein konstantes Muster, ein Typmuster, ein variables Muster und ein leeres variables Muster.
- Beispiele können in is-Ausdrücken und in case-Blöcken verwendet werden.
- Die Implementierung des konstanten Musters im Ausdruck für Werttypen ist hinsichtlich der Leistung alles andere als ideal.
- Stichproben einer Variablen stimmen immer überein, man muss vorsichtig mit ihnen sein.
- Mit der switch-Anweisung können Typprüfungen mit zusätzlichen Prädikaten in when-Klauseln festgelegt werden.
Unity Event in Moskau - Unity Moscow Meetup 2018.1
Am Donnerstag, dem 11. Oktober, findet an der Higher School of Economics das Unity Moscow Meetup 2018.1 statt. Dies ist das erste Treffen der Unity-Entwickler in dieser Saison in Moskau. Das Thema des ersten Mitaps wird AR / VR sein. Sie finden interessante Berichte, Kommunikation mit Branchenfachleuten sowie eine spezielle Demo-Zone von MSI.
Details