In einer kürzlich veröffentlichten Version des DotNet & More Blazor-Podcasts, NetCore 3.0 Preview, C # 8, haben wir nicht nur beiläufig ein so brennendes Thema wie C # 8 erwähnt. Die Geschichte über die Erfahrung mit C # 8 war nicht groß genug, um ein separates Thema zu behandeln, daher wurde beschlossen, die Mittel des Genres der Pistole mit ihm zu teilen.
In diesem Artikel möchte ich über meine Erfahrungen mit C # 8 in der Produktion für 4 Monate sprechen. Nachfolgend finden Sie Antworten auf folgende Fragen:
- Wie man im neuen C # "buchstabiert"
- Welche Funktionen waren wirklich nützlich
- Was enttäuscht
Eine vollständige Liste der C # 8-Funktionen finden Sie in der offiziellen Dokumentation von Microsoft . In diesem Artikel werde ich die Möglichkeiten auslassen, die ich aus dem einen oder anderen Grund nicht ausprobieren konnte, nämlich:
- Readonly Mitglieder
- Standardschnittstellenmitglieder
- Einweg-Ref-Strukturen
- Asynchrone Streams
- Indizes und Bereiche
Ich schlage vor, mit einer der köstlichsten Möglichkeiten zu beginnen, wie es mir vorher schien.
Ausdrücke wechseln
In unseren Träumen präsentieren wir diese Funktion ziemlich rosig:
int Exec(Operation operation, int x, int y) => operation switch { Operation.Summ => x + y, Operation.Diff => x - y, Operation.Mult => x * y, Operation.Div => x / y, _ => throw new NotSupportedException() };
Leider nimmt die Realität ihre eigenen Anpassungen vor.
Erstens gibt es keine Möglichkeit, die Bedingungen zu kombinieren:
string TrafficLights(Signal signal) { switch (signal) { case Signal.Red: case Signal.Yellow: return "stop"; case Signal.Green: return "go"; default: throw new NotSupportedException(); } }
In der Praxis bedeutet dies, dass in der Hälfte der Fälle der Schalterausdruck in einen regulären Schalter umgewandelt werden muss, um ein Kopieren und Einfügen zu vermeiden.
Zweitens unterstützt die neue Syntax keine Anweisungen, d. H. Code, der keinen Wert zurückgibt. Es scheint gut, und es ist nicht notwendig, aber ich selbst war überrascht, als mir klar wurde, wie oft Schalter (in Verbindung mit Mustervergleich) für so etwas wie die Behauptung in Tests verwendet werden.
Drittens unterstützt der Schalterausdruck, der aus dem letzten Absatz folgt, keine mehrzeiligen Handler. Wie beängstigend wir zum Zeitpunkt des Hinzufügens der Protokolle verstehen:
int ExecFull(Operation operation, int x, int y) { switch (operation) { case Operation.Summ: logger.LogTrace("{x} + {y}", x, y); return x + y; case Operation.Diff: logger.LogTrace("{x} - {y}", x, y); return x - y; case Operation.Mult: logger.LogTrace("{x} * {y}", x, y); return x * y; case Operation.Div: logger.LogTrace("{x} / {y}", x, y); return x / y; default: throw new NotSupportedException(); } }
Ich möchte nicht sagen, dass der neue Schalter schlecht ist. Nein, er ist gut, nur nicht gut genug.
Eigenschafts- und Positionsmuster
Vor einem Jahr schienen sie mir die Hauptkandidaten für den Titel "Gelegenheit, die die Entwicklung verändert hat" zu sein. Und wie erwartet müssen Sie Ihren Entwicklungsansatz ändern, um die volle Leistung von Positions- und Eigenschaftsmustern nutzen zu können. Es ist nämlich notwendig, algebraische Datentypen zu imitieren.
Es scheint, was ist das Problem: Nehmen Sie die Marker-Oberfläche und gehen Sie. Leider hat diese Methode in einem großen Projekt einen schwerwiegenden Nachteil: Niemand garantiert, dass die Erweiterung Ihrer algebraischen Typen in der Entwurfszeit verfolgt wird. Es ist daher sehr wahrscheinlich, dass Änderungen am Code im Laufe der Zeit an den unerwartetsten Stellen zu vielen "Standardfehlern" führen.
Tupelmuster
Aber der "jüngere Bruder" der neuen Vergleichsmöglichkeiten mit der Stichprobe erwies sich als wirklich gut gemacht. Die Sache ist, dass das Tupelmuster keine Änderungen in der bekannten Architektur unseres Codes erfordert, sondern nur einige Fälle vereinfacht:
Player? Play(Gesture left, Gesture right) { switch (left, right) { case (Gesture.Rock, Gesture.Rock): case (Gesture.Paper, Gesture.Paper): case (Gesture.Scissors, Gesture.Scissors): return null; case (Gesture.Rock, Gesture.Scissors): case (Gesture.Scissors, Gesture.Paper): case (Gesture.Paper, Gesture.Rock): return Player.Left; case (Gesture.Paper, Gesture.Scissors): case (Gesture.Rock, Gesture.Paper): case (Gesture.Scissors, Gesture.Rock): return Player.Right; default: throw new NotSupportedException(); } }
Das Beste daran ist jedoch, dass diese Funktion, die vorhersehbar genug ist, mit der Deconstruct-Methode hervorragend funktioniert. Übergeben Sie einfach eine Klasse mit implementiertem Deconstruct, um die Funktionen des Tupelmusters zu wechseln und zu nutzen.
Deklarationen verwenden
Es scheint eine kleine Chance zu sein, aber es bringt so viel Freude. In allen Promos spricht Microsoft von einem Aspekt wie der Reduzierung der Verschachtelung. Aber seien wir ehrlich, nicht so sehr wichtig. Aber was wirklich ernst ist, sind die Nebenwirkungen des Ausschlusses eines Codeblocks:
- Wenn wir using hinzufügen, müssen wir den Code häufig mit der Copy-Paste-Methode "innerhalb" des Blocks ziehen. Jetzt denken wir einfach nicht darüber nach
- Die Variablen, die innerhalb von using deklariert und nach dem Entsorgen des using-Objekts verwendet werden, bereiten echte Kopfschmerzen. Ein Problem weniger
- In Klassen, die häufige Dispose-Aufrufe erfordern, wäre jede Methode 2 Zeilen länger. Es scheint eine Kleinigkeit zu sein, aber unter den Bedingungen vieler kleiner Methoden erlaubt diese Kleinigkeit nicht, eine ausreichende Anzahl dieser Methoden auf einem Bildschirm anzuzeigen
Infolgedessen ändert eine so einfache Sache wie die Verwendung von Deklarationen das Gefühl der Codierung so sehr, dass Sie einfach nicht zu c # 7.3 zurückkehren möchten.
Statische lokale Funktionen
Um ehrlich zu sein, würde ich diese Möglichkeit ohne die Hilfe der Code-Analyse nicht einmal bemerken. Trotzdem hat sie sich fest in meinem Code verankert: Schließlich sind statische lokale Funktionen perfekt für die Rolle kleiner reiner Funktionen geeignet, da sie das Schließen von Methodenvariablen nicht unterstützen können. Dies schont das Herz, da Sie verstehen, dass Ihr Code einen potenziellen Fehler weniger enthält.
Nullable Referenztypen
Und zum Nachtisch möchte ich das wichtigste Merkmal von C # 8 erwähnen. In Wahrheit verdient das Parsen von nullbaren Referenztypen einen separaten Artikel. Ich möchte nur die Empfindungen beschreiben.
Zusammenfassung
Natürlich erreichen die sich bietenden Möglichkeiten keine vollwertige Revolution, aber es gibt immer weniger Lücken zwischen C # und F # / Scala. Ob es gut oder schlecht ist, wird die Zeit zeigen.
Zum Zeitpunkt der Veröffentlichung dieses Artikels hat sich C # 8 möglicherweise bereits in Ihrem Projekt niedergelassen. Ich würde mich also fragen, was Sie von der neuen Version unserer Lieblingssprache halten.