Neues in C# 7.0What's new in C# 7.0

C# 7.0 bietet eine Reihe von neuen Features für die C#-Programmiersprache:C# 7.0 adds a number of new features to the C# language:

  • outVariablenout variables
    • Sie können out-Werte als Inlineargumente für die Methode deklarieren, wenn sie verwendet werden.You can declare out values inline as arguments to the method where they're used.
  • TupelTuples
    • Sie können einfache, unbenannte Typen erstellen, die mehrere öffentliche Felder enthalten.You can create lightweight, unnamed types that contain multiple public fields. Compiler und IDE-Tools kennen die Semantik dieser Typen.Compilers and IDE tools understand the semantics of these types.
  • VerwerfenDiscards
    • Ausschussvariablen (discards) sind temporäre, lesegeschützte Variablen, die in Zuweisungen verwendet werden, wenn der zugewiesene Wert nicht weiter interessiert.Discards are temporary, write-only variables used in assignments when you don't care about the value assigned. Sie eignen sich besonders zum Dekonstruieren von Tupeln und benutzerdefinierten Typen sowie beim Aufrufen von Methoden mit out-Parametern.They're most useful when deconstructing tuples and user-defined types, as well as when calling methods with out parameters.
  • MustervergleichPattern Matching
    • Sie können Verzweigungslogik basierend auf beliebigen Typen und Werten der Member dieser Typen erstellen.You can create branching logic based on arbitrary types and values of the members of those types.
  • Lokale ref-Variablen und Rückgabetypenref locals and returns
    • Lokale Variablen und Rückgabewerte von Methoden können Verweise auf andere Speicher sein.Method local variables and return values can be references to other storage.
  • Lokale FunktionenLocal Functions
    • Sie können Funktionen innerhalb von anderen Funktionen verschachteln, um deren Bereich und Sichtbarkeit zu beschränken.You can nest functions inside other functions to limit their scope and visibility.
  • Mehr AusdruckskörpermemberMore expression-bodied members
    • Die Liste der Member, die mithilfe von Ausdrücken erstellt werden können, ist länger geworden.The list of members that can be authored using expressions has grown.
  • throw-Ausdrückethrow Expressions
    • Sie können Ausnahmen in Codekonstrukten auslösen, die vorher nicht zulässig waren, da throw eine Anweisung war.You can throw exceptions in code constructs that previously weren't allowed because throw was a statement.
  • Generalisierte asynchrone RückgabetypenGeneralized async return types
    • Methoden, die mit dem async-Modifizierer deklariert werden, können zusätzlich zu Task und Task<T> andere Typen zurückgeben.Methods declared with the async modifier can return other types in addition to Task and Task<T>.
  • Verbesserung der numerischen literalen SyntaxNumeric literal syntax improvements
    • Neue Token verbessern die Lesbarkeit für numerische Konstanten.New tokens improve readability for numeric constants.

Dieser Artikel enthält im Folgenden eine Übersicht über die einzelnen Funktionen.The remainder of this article provides an overview of each feature. Sie werden die Hintergründe jeder einzelnen Funktion erfahren.For each feature, you'll learn the reasoning behind it. Sie werden die Syntax erlernen.You'll learn the syntax. Sie können sich diese Funktionen in unserer Umgebung mit dem globalen dotnet try-Tool näher ansehen:You can explore these features in your environment using the dotnet try global tool:

  1. Installieren Sie das globale dotnet-try-Tool.Install the dotnet-try global tool.
  2. Klonen Sie das dotnet/try-samples-Repository.Clone the dotnet/try-samples repository.
  3. Legen Sie das aktuelle Verzeichnis auf das Unterverzeichnis csharp7 für das try-samples-Repository fest.Set the current directory to the csharp7 subdirectory for the try-samples repository.
  4. Führen Sie dotnet try aus.Run dotnet try.

out-Variablenout variables

Die vorhandene Syntax zur Unterstützung von out-Parametern wurde in dieser Version verbessert.The existing syntax that supports out parameters has been improved in this version. Nun können Sie out-Variablen in der Argumentliste eines Methodenaufrufs deklarieren, anstatt eine separate Deklarationsanweisung zu schreiben:You can now declare out variables in the argument list of a method call, rather than writing a separate declaration statement:

if (int.TryParse(input, out int result))
    Console.WriteLine(result);
else
    Console.WriteLine("Could not parse input");

Möglicherweise möchten Sie den Typ der out-Variablen aus Gründen der Übersichtlichkeit angeben, wie oben gezeigt.You may want to specify the type of the out variable for clarity, as shown above. Die Sprache unterstützt jedoch die Verwendung einer implizit typisierten lokalen Variable:However, the language does support using an implicitly typed local variable:

if (int.TryParse(input, out var answer))
    Console.WriteLine(answer);
else
    Console.WriteLine("Could not parse input");
  • Der Code ist einfacher zu lesen.The code is easier to read.
    • Sie deklarieren die out-Variable, wenn Sie sie verwenden, nicht in einer anderen Zeile weiter oben.You declare the out variable where you use it, not on another line above.
  • Sie müssen keinen Anfangswert zuweisen.No need to assign an initial value.
    • Durch das Deklarieren der out-Variable, wenn sie in einem Methodenaufruf verwendet wird, können Sie diese nicht versehentlich verwenden, bevor sie zugewiesen wurde.By declaring the out variable where it's used in a method call, you can't accidentally use it before it is assigned.

TupelTuples

C# bietet eine umfangreiche Syntax für Klassen und Strukturen, die verwendet wird, um Ihre Entwurfsabsicht zu erläutern.C# provides a rich syntax for classes and structs that is used to explain your design intent. Aber manchmal erfordert diese umfangreiche Syntax zusätzliche Arbeit mit minimalem Nutzen.But sometimes that rich syntax requires extra work with minimal benefit. Möglicherweise schreiben Sie häufig Methoden, die eine einfache Struktur erfordern, die mehr als ein Datenelement enthält.You may often write methods that need a simple structure containing more than one data element. Zur Unterstützung dieser Szenarios wurden C# Tupel hinzugefügt.To support these scenarios tuples were added to C#. Tupel sind einfache Datenstrukturen, die mehrere Felder zur Darstellung der Datenmember enthalten.Tuples are lightweight data structures that contain multiple fields to represent the data members. Die Felder werden nicht überprüft, und Sie können keine eigenen Methoden definieren.The fields aren't validated, and you can't define your own methods

Hinweis

Tupel waren schon vor C# 7.0 verfügbar, sie waren jedoch ineffizient und hatten keine Sprachunterstützung.Tuples were available before C# 7.0, but they were inefficient and had no language support. Das brachte mit sich, dass auf Tupelelemente nur als Item1, Item2 usw. verwiesen werden konnte.This meant that tuple elements could only be referenced as Item1, Item2 and so on. Mit C# 7.0 wird Sprachunterstützung für Tupel eingeführt, wodurch semantische Namen für die Felder eines Tupels mit Einsatz neuer, effizienterer Tupeltypen möglich werden.C# 7.0 introduces language support for tuples, which enables semantic names for the fields of a tuple using new, more efficient tuple types.

Sie können ein Tupel erstellen, indem Sie jedem Member einen Wert zuweisen und ihnen optional auch semantische Namen bereitstellen:You can create a tuple by assigning a value to each member, and optionally providing semantic names to each of the members of the tuple:

(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

Das namedLetters-Tupel enthält Felder, die als Alpha und Beta bezeichnet werden.The namedLetters tuple contains fields referred to as Alpha and Beta. Diese Namen bestehen nur zur Kompilierzeit und werden nicht beibehalten, wenn das Tupel beispielsweise zur Laufzeit mithilfe von Reflektion untersucht wird.Those names exist only at compile time and aren't preserved, for example when inspecting the tuple using reflection at runtime.

In einer Tupelzuweisung können Sie auch die Namen der Felder auf der rechten Seite der Zuweisung angeben:In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment:

var alphabetStart = (Alpha: "a", Beta: "b");
Console.WriteLine($"{alphabetStart.Alpha}, {alphabetStart.Beta}");

Manchmal möchten Sie vielleicht die Member eines Tupels entpacken, die von einer Methode zurückgegeben wurden.There may be times when you want to unpackage the members of a tuple that were returned from a method. Sie können dazu für jeden Wert im Tupel separate Variablen deklarieren.You can do that by declaring separate variables for each of the values in the tuple. Das Auspacken wird als Dekonstruieren des Tupels bezeichnet:This unpackaging is called deconstructing the tuple:

(int max, int min) = Range(numbers);
Console.WriteLine(max);
Console.WriteLine(min);

Sie können auch eine ähnliche Dekonstruktion für alle Typen in .NET bereitstellen.You can also provide a similar deconstruction for any type in .NET. Schreiben Sie eine Deconstruct-Methode als Member der Klasse.You write a Deconstruct method as a member of the class. Diese Deconstruct-Methode bietet eine Reihe von out-Argumenten für jede der Eigenschaften, die Sie extrahieren möchten.That Deconstruct method provides a set of out arguments for each of the properties you want to extract. Berücksichtigen Sie diese Point-Klasse, die eine Dekonstruktionsmethode bereitstellt, die die X- und Y-Koordinaten extrahiert:Consider this Point class that provides a deconstructor method that extracts the X and Y coordinates:

   public class Point
   {
       public Point(double x, double y) 
           => (X, Y) = (x, y);

       public double X { get; }
       public double Y { get; }

       public void Deconstruct(out double x, out double y) =>
           (x, y) = (X, Y);
   }

Sie können die einzelnen Felder extrahieren, indem Sie einem Point ein Tupel zuweisen:You can extract the individual fields by assigning a Point to a tuple:

var p = new Point(3.14, 2.71);
(double X, double Y) = p;

Ausführliche Informationen zu Tupeln finden Sie im Artikel zu Tupeln.You can learn more in depth about tuples in the tuples article.

AusschussDiscards

Beim Dekonstruieren eines Tupels oder dem Aufrufen einer Methode mit out-Parametern sind Sie gezwungen, eine Variable zu definieren, deren Wert Sie nicht interessiert und die Sie nicht zu verwenden beabsichtigen.Often when deconstructing a tuple or calling a method with out parameters, you're forced to define a variable whose value you don't care about and don't intend to use. C# verfügt jetzt über Unterstützung für Ausschussvariablen (discards), um diesem Szenario Rechnung zu tragen.C# adds support for discards to handle this scenario. Eine Ausschussvariable ist eine lesegeschützte Variable mit dem Namen _ (dem Unterstrichzeichen); Sie können der einzelnen Variablen alle Werte zuweisen, die Sie verwerfen möchten.A discard is a write-only variable whose name is _ (the underscore character); you can assign all of the values that you intend to discard to the single variable. Eine Ausschussvariable ist wie eine nicht zugewiesene Variable; abgesehen von der Zuweisungsanweisung kann die Ausschussvariable nicht in Code verwendet werden.A discard is like an unassigned variable; apart from the assignment statement, the discard can't be used in code.

Ausschussvariablen werden in den folgenden Szenarien unterstützt:Discards are supported in the following scenarios:

  • Beim Dekonstruieren von Tupeln oder benutzerdefinierten Typen.When deconstructing tuples or user-defined types.
  • Beim Aufrufen von Methoden mit out-Parametern.When calling methods with out parameters.
  • In einem Musterabgleichsvorgang mit den Anweisungen is und switch.In a pattern matching operation with the is and switch statements.
  • Als eigenständiger Bezeichner, wenn Sie den Wert einer Zuweisung explizit als Ausschuss kennzeichnen möchten.As a standalone identifier when you want to explicitly identify the value of an assignment as a discard.

Das folgende Beispiel definiert eine QueryCityDataForYears-Methode, die ein 6-Tupel zurückgibt, das ein Datum für eine Stadt für zwei verschiedene Jahre enthält.The following example defines a QueryCityDataForYears method that returns a 6-tuple that contains data for a city for two different years. Der Methodenaufruf im Beispiel befasst sich nur mit den zwei Bevölkerungswerten, die von der Methode zurückgegeben werden und behandelt so die verbleibenden Werte im Tupel beim Dekonstruieren des Tupels als Ausschuss.The method call in the example is concerned only with the two population values returned by the method and so treats the remaining values in the tuple as discards when it deconstructs the tuple.

using System;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
        var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

        Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
    }
   
    private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
    {
        int population1 = 0, population2 = 0;
        double area = 0;
      
        if (name == "New York City")
        {
            area = 468.48; 
            if (year1 == 1960)
            {
                population1 = 7781984;
            }
            if (year2 == 2010)
            {
                population2 = 8175133;
            }
            return (name, area, year1, population1, year2, population2);
        }

        return ("", 0, 0, 0, 0, 0);
    }
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149

Weitere Informationen finden Sie unter Ausschuss.For more information, see Discards.

MusterabgleichPattern matching

Mustervergleich ist ein Funktion, mit der Sie Methodenverteilung für andere Eigenschaften als den Typ eines Objekts implementieren können.Pattern matching is a feature that allows you to implement method dispatch on properties other than the type of an object. Sie sind wahrscheinlich bereits vertraut mit Methodenverteilung, die auf dem Typ eines Objekts basiert.You're probably already familiar with method dispatch based on the type of an object. In der objektorientierten Programmierung stellen virtuelle und überschreibende Methoden Sprachsyntax bereit, um die Abfertigung der Methoden basierend auf dem Typ eines Objekts zu implementieren.In object-oriented programming, virtual and override methods provide language syntax to implement method dispatching based on an object's type. Basis- und abgeleitete Klassen stellen verschiedene Implementierungen bereit.Base and Derived classes provide different implementations. Mustervergleichsausdrücke erweitern dieses Konzept, sodass Sie ähnliche Verteilungsmuster für Typen und Datenelemente, die nicht durch eine Vererbungshierarchie verknüpft sind, einfach implementieren können.Pattern matching expressions extend this concept so that you can easily implement similar dispatch patterns for types and data elements that aren't related through an inheritance hierarchy.

Mustervergleich unterstützt is-Ausdrücke und switch-Ausdrücke.Pattern matching supports is expressions and switch expressions. Jeder davon ermöglicht das Überprüfen eines Objekts und dessen Eigenschaften, um zu bestimmen, ob das Objekt dem gesuchten Muster entspricht.Each enables inspecting an object and its properties to determine if that object satisfies the sought pattern. Sie verwenden das when-Schlüsselwort, um zusätzliche Regeln für das Muster anzugeben.You use the when keyword to specify additional rules to the pattern.

Der Musterausdruck is erweitert den vertrauten is-Operator, um eine Abfrage zum Typ eines Objekts auszuführen und das Ergebnis in einer Anweisung zuzuweisen.The is pattern expression extends the familiar is operator to query an object about its type and assign the result in one instruction. Der folgende Code überprüft, ob es sich bei einer Variablen um einen int-Wert handelt und fügt sie, wenn dies der Fall ist, der aktuellen Summe hinzu:The following code checks if a variable is an int, and if so, adds it to the current sum:

if (input is int count)
    sum += count;

Das vorausgegangene kleine Beispiel verdeutlicht die Verbesserungen des is-Ausdrucks.The preceding small example demonstrates the enhancements to the is expression. Sie können für Wert- und Verweistypen testen und das erfolgreiche Ergebnis einer neuen Variable des richtigen Typs zuweisen.You can test against value types as well as reference types, and you can assign the successful result to a new variable of the correct type.

Der Switch-Vergleichsausdruck verfügt über eine vertraute Syntax basierend auf der switch-Anweisung, die bereits Teil der C#-Sprache ist.The switch match expression has a familiar syntax, based on the switch statement already part of the C# language. Die aktualisierte Switch-Anweisung verfügt über mehrere neue Konstrukte:The updated switch statement has several new constructs:

  • Der maßgebliche Typ eines switch-Ausdrucks ist nicht mehr beschränkt auf ganzzahlige Typen, Enum-Typen, string oder einen Nullable-Typ, der einem dieser Typen entspricht.The governing type of a switch expression is no longer restricted to integral types, Enum types, string, or a nullable type corresponding to one of those types. Es kann jeder Typ verwendet werden.Any type may be used.
  • Sie können den Typ des switch-Ausdrucks in jeder case-Bezeichnung testen.You can test the type of the switch expression in each case label. Sie können diesem Typ wie schon beim is-Ausdruck eine neue Variable zuweisen.As with the is expression, you may assign a new variable to that type.
  • Wenn Sie die Bedingungen für diese Variable weiter testen möchten, können Sie eine when-Klausel hinzufügen.You may add a when clause to further test conditions on that variable.
  • Die Reihenfolge der case-Bezeichnungen ist hierbei entscheidend.The order of case labels is now important. Die erste Verzweigung, für die eine Übereinstimmung gefunden wird, wird ausgeführt. Alle weiteren werden übersprungen.The first branch to match is executed; others are skipped.

Im folgenden Code werden diese Funktionen veranschaulicht:The following code demonstrates these new features:

public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
        switch (i)
        {
            case 0:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
        }
    }
    return sum;
}
  • case 0: ist das vertraute Konstantenmuster.case 0: is the familiar constant pattern.
  • case IEnumerable<int> childSequence: ist ein Typmuster.case IEnumerable<int> childSequence: is a type pattern.
  • case int n when n > 0: ist ein Typmuster mit einer zusätzlichen when-Bedingung.case int n when n > 0: is a type pattern with an additional when condition.
  • case null: ist das NULL-Muster.case null: is the null pattern.
  • default: ist die vertraute Standardanfrage.default: is the familiar default case.

Weitere Informationen zum Mustervergleich finden Sie unter Pattern Matching in C# (Mustervergleich in C#).You can learn more about pattern matching in Pattern Matching in C#.

Lokale ref-Variablen und RückgabetypenRef locals and returns

Diese Funktion ermöglicht Algorithmen, die Verweise auf Variablen verwenden und zurückgeben, die an anderer Stelle definiert sind.This feature enables algorithms that use and return references to variables defined elsewhere. Ein Beispiel ist das Arbeiten mit großen Matrizen und die Suche nach einem einzigen Ort mit bestimmten Eigenschaften.One example is working with large matrices, and finding a single location with certain characteristics. Die folgende Methode gibt einen Verweis auf diesen Speicher in der Matrix zurück:The following method returns a reference to that storage in the matrix:

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

Sie können den Rückgabewert als ref deklarieren und diesen Wert wie im folgenden Code gezeigt in der Matrix ändern:You can declare the return value as a ref and modify that value in the matrix, as shown in the following code:

ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix[4, 2]);

Die C#-Sprache verfügt über mehrere Regeln, die Sie vor einer falschen Verwendung der lokalen ref-Variablen und Rückgabetypen schützen:The C# language has several rules that protect you from misusing the ref locals and returns:

  • Sie müssen der Methodensignatur und allen ref-Anweisungen einer Methode das return-Schlüsselwort hinzufügen.You must add the ref keyword to the method signature and to all return statements in a method.
    • Dadurch wird deutlich, dass Rückgaben in der gesamten Methode als Verweis erfolgen.That makes it clear the method returns by reference throughout the method.
  • Eine ref return-Rückgabe kann einer Wert- oder einer ref-Variablen zugewiesen werden.A ref return may be assigned to a value variable, or a ref variable.
    • Der Aufrufer steuert, ob der Rückgabewert kopiert werden soll.The caller controls whether the return value is copied or not. Durch Auslassen des ref-Modifizierers beim Zuweisen des Rückgabewerts gibt der Aufrufer an, dass der Wert kopiert werden und nicht etwa ein Verweis auf den Speicher erfolgen soll.Omitting the ref modifier when assigning the return value indicates that the caller wants a copy of the value, not a reference to the storage.
  • Sie können einer lokalen ref-Variablen keinen Rückgabewert einer Standardmethode zuweisen.You can't assign a standard method return value to a ref local variable.
    • Dadurch werden Aussagen wie ref int i = sequence.Count(); nicht zugelassen.That disallows statements like ref int i = sequence.Count();
  • Sie können ref nicht an eine Variable zurückgeben, deren Lebensdauer nicht über die Ausführung der Methode hinausgeht.You can't return a ref to a variable whose lifetime doesn't extend beyond the execution of the method.
    • Das bedeutet, dass Sie keinen Verweis auf eine lokale Variable oder eine Variable mit einem ähnlichen Bereich zurückgeben können.That means you can't return a reference to a local variable or a variable with a similar scope.
  • Lokale ref-Variablen und Rückgabewerte können nicht in Verbindung mit asynchronen Methoden verwendet werden.ref locals and returns can't be used with async methods.
    • Der Compiler kann nicht feststellen, ob die Variable, auf die verwiesen wird, bei der Rückgabe der asynchronen Methode auf ihren endgültigen Wert festgelegt ist.The compiler can't know if the referenced variable has been set to its final value when the async method returns.

Das Hinzufügen von lokalen ref-Variablen und ref-Rückgaben ermöglicht effizientere Algorithmen, da Werte nicht kopiert oder dereferenzierende Vorgänge nicht mehrmals ausgeführt werden.The addition of ref locals and ref returns enables algorithms that are more efficient by avoiding copying values, or performing dereferencing operations multiple times.

Das Hinzufügen von ref zum Rückgabewert stellt eine quellkompatible Änderung dar.Adding ref to the return value is a source compatible change. Vorhandener Code lässt sich kompilieren, der ref-Rückgabewert wird aber bei der Zuweisung kopiert.Existing code compiles, but the ref return value is copied when assigned. Aufrufer müssen den Speicher für den Rückgabewert in eine lokale ref-Variable aktualisieren, um die Rückgabe als Verweis zu speichern.Callers must update the storage for the return value to a ref local variable to store the return as a reference.

Weitere Informationen finden Sie im Artikel Schlüsselwort „ref“.For more information, see the ref keyword article.

Lokale FunktionenLocal functions

Viele Entwürfe für Klassen enthalten Methoden, die nur von einer Stelle aufgerufen werden.Many designs for classes include methods that are called from only one location. Durch diese zusätzlichen privaten Methoden bleibt jede Methode klein und konzentriert.These additional private methods keep each method small and focused. Mit lokalen Funktionen können Sie Methoden innerhalb des Kontexts einer anderen Methode deklarieren.Local functions enable you to declare methods inside the context of another method. So können Leser der Klasse leichter erkennen, dass die lokale Methode nur aus dem Kontext aufgerufen wird, in dem sie deklariert wird.Local functions make it easier for readers of the class to see that the local method is only called from the context in which it is declared.

Es gibt zwei häufige Anwendungsfälle für lokale Funktionen: öffentliche Iteratormethoden und öffentliche asynchrone Methoden.There are two common use cases for local functions: public iterator methods and public async methods. Beide Arten von Methoden generieren Code, der Fehler später meldet, als Programmierer erwarten würden.Both types of methods generate code that reports errors later than programmers might expect. Bei Iteratormethoden werden Ausnahmen nur festgestellt, wenn Code aufgerufen wird, der die zurückgegebene Sequenz auflistet.In iterator methods, any exceptions are observed only when calling code that enumerates the returned sequence. Bei asynchronen Methoden werden Ausnahmen nur festgestellt, wenn der zurückgegebene Task-Wert erwartet wird.In async methods, any exceptions are only observed when the returned Task is awaited. Im folgenden Beispiel wird veranschaulicht, wie mithilfe einer lokalen Funktion die Überprüfung der Parameter von der Iteratorimplementierung getrennt wird:The following example demonstrates separating parameter validation from the iterator implementation using a local function:

public static IEnumerable<char> AlphabetSubset3(char start, char end)
{
    if (start < 'a' || start > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end < 'a' || end > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

    if (end <= start)
        throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

    return alphabetSubsetImplementation();

    IEnumerable<char> alphabetSubsetImplementation()
    {
        for (var c = start; c < end; c++)
            yield return c;
    }
}

Das gleiche Verfahren kann mit async-Methoden eingesetzt werden, um sicherzustellen, dass Ausnahmen aufgrund der Argumentüberprüfung ausgelöst werden, bevor die asynchrone Arbeit beginnt:The same technique can be employed with async methods to ensure that exceptions arising from argument validation are thrown before the asynchronous work begins:

public Task<string> PerformLongRunningWork(string address, int index, string name)
{
    if (string.IsNullOrWhiteSpace(address))
        throw new ArgumentException(message: "An address is required", paramName: nameof(address));
    if (index < 0)
        throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

    return longRunningWorkImplementation();

    async Task<string> longRunningWorkImplementation()
    {
        var interimResult = await FirstWork(address);
        var secondResult = await SecondStep(index, name);
        return $"The results are {interimResult} and {secondResult}. Enjoy.";
    }
}

Hinweis

Einige der Entwürfe, die von lokalen Funktionen unterstützt werden, könnten auch mithilfe von Lambdaausdrücken erreicht werden.Some of the designs that are supported by local functions could also be accomplished using lambda expressions. Weitere Informationen finden Sie unter Lokale Funktionen im Vergleich zu Lambdaausdrücken.For more information, see Local functions vs. lambda expressions.

Mehr AusdruckskörpermemberMore expression-bodied members

In C# 6 wurden Ausdruckskörpermember für Memberfunktionen und schreibgeschützte Eigenschaften eingeführt.C# 6 introduced expression-bodied members for member functions, and read-only properties. Mit C# 7.0 werden die zulässigen Member erweitert, die als Ausdrücke implementiert werden können.C# 7.0 expands the allowed members that can be implemented as expressions. In C# 7.0 können Sie Konstruktoren, Finalizer sowie get- und set-Zugriffsmethoden für Eigenschaften und Indexer implementieren.In C# 7.0, you can implement constructors, finalizers, and get and set accessors on properties and indexers. Der folgende Code zeigt entsprechende Beispiele:The following code shows examples of each:

// Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;

// Expression-bodied finalizer
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");

private string label;

// Expression-bodied get / set accessors.
public string Label
{
    get => label;
    set => this.label = value ?? "Default label";
}

Hinweis

In diesem Beispiel ist kein Finalizer erforderlich, aber damit soll die Syntax dargestellt werden.This example does not need a finalizer, but it is shown to demonstrate the syntax. Sie sollten in Ihrer Klasse nur einen Finalizer implementieren, wenn es notwendig ist, nicht verwaltete Ressourcen freizugeben.You should not implement a finalizer in your class unless it is necessary to release unmanaged resources. Sie sollten auch die Verwendung der SafeHandle-Klasse in Betracht ziehen, anstatt nicht verwaltete Ressourcen direkt zu verwalten.You should also consider using the SafeHandle class instead of managing unmanaged resources directly.

Diese neuen Speicherorte für Ausdruckskörpermember sind ein wichtiger Meilenstein für C#: Diese Funktionen wurden von Community-Mitgliedern implementiert, die am Open-Source-Projekt Roslyn arbeiten.These new locations for expression-bodied members represent an important milestone for the C# language: These features were implemented by community members working on the open-source Roslyn project.

Das Ändern einer Methode in ein Ausdruckskörpermember ist eine binärkompatible Änderung.Changing a method to an expression bodied member is a binary compatible change.

Throw-AusdrückeThrow expressions

In C# ist throw schon immer eine Anweisung.In C#, throw has always been a statement. Da throw eine Anweisung und kein Ausdruck ist, gab es C#-Konstrukte, in denen diese Anweisung nicht verwendet werden konnte.Because throw is a statement, not an expression, there were C# constructs where you couldn't use it. Darunter waren bedingte Ausdrücke, NULL-Sammelausdrücke und einige Lambdaausdrücke.These included conditional expressions, null coalescing expressions, and some lambda expressions. Das Hinzufügen von Ausdruckskörpermembern fügt mehr Speicherorte hinzu, bei denen throw-Ausdrücke nützlich wären.The addition of expression-bodied members adds more locations where throw expressions would be useful. Damit Sie diese Konstrukte schreiben können, wurden in C# 7.0 Throw-Ausdrücke eingeführt.So that you can write any of these constructs, C# 7.0 introduces throw expressions.

Diese Ergänzung erleichtert das Schreiben ausdrucksbasierteren Codes.This addition makes it easier to write more expression-based code. Sie benötigen zur Fehlerüberprüfung keine weiteren Anweisungen.You don't need additional statements for error checking.

Generalisierte asynchrone RückgabetypenGeneralized async return types

Das Zurückgeben eines Task-Objekts von asynchronen Methoden kann Leistungsengpässe in bestimmten Pfaden verursachen.Returning a Task object from async methods can introduce performance bottlenecks in certain paths. Task ist ein Verweistyp, seine Verwendung bedeutet also das Zuordnen eines Objekts.Task is a reference type, so using it means allocating an object. In Fällen, in denen eine mit dem async-Modifizierer deklarierte Methode ein zwischengespeichertes Ergebnis zurückgibt oder synchron abschließt, können die zusätzlichen Zuordnungen viel Zeit bei der Ausführung kritischer Codeabschnitte kosten.In cases where a method declared with the async modifier returns a cached result, or completes synchronously, the extra allocations can become a significant time cost in performance critical sections of code. Es kann kostspielig werden, wenn diese Zuordnungen in engen Schleifen auftreten.It can become costly if those allocations occur in tight loops.

Durch die neue Sprachfunktion sind die Rückgabetypen asynchroner Methoden nicht auf Task, Task<T> und void beschränkt.The new language feature means that async method return types aren't limited to Task, Task<T>, and void. Der zurückgegebene Typ muss noch immer das asynchrone Muster erfüllen, d.h. dass eine GetAwaiter-Methode verfügbar sein muss.The returned type must still satisfy the async pattern, meaning a GetAwaiter method must be accessible. Als konkretes Beispiel wurde der ValueTask-Typ zu .NET hinzugefügt, um diese neue Sprachfunktion zu nutzen:As one concrete example, the ValueTask type has been added to .NET to make use of this new language feature:

public async ValueTask<int> Func()
{
    await Task.Delay(100);
    return 5;
}

Hinweis

Sie müssen das NuGet-Paket hinzufügen System.Threading.Tasks.Extensions, um den Typ ValueTask<TResult> verwenden zu können.You need to add the NuGet package System.Threading.Tasks.Extensions in order to use the ValueTask<TResult> type.

Diese Verbesserung ist besonders für Bibliotheksautoren hilfreich, um die Zuordnung von Task in leistungskritischem Code zu verhindern.This enhancement is most useful for library authors to avoid allocating a Task in performance critical code.

Verbesserung der numerischen literalen SyntaxNumeric literal syntax improvements

Falsches Lesen numerischer Konstanten kann Code beim ersten Lesen schwerer verständlich machen.Misreading numeric constants can make it harder to understand code when reading it for the first time. Bitmasken oder andere symbolische Werte können Missverständnisse hervorrufen.Bit masks or other symbolic values are prone to misunderstanding. C# 7.0 enthält zwei neue Funktionen, mit denen Zahlen für den beabsichtigten Zweck so lesbar wie möglich geschrieben werden können: binäre Literale und Zifferntrennzeichen.C# 7.0 includes two new features to write numbers in the most readable fashion for the intended use: binary literals, and digit separators.

Beim Erstellen von Bitmasken oder wenn die binäre Darstellung einer Zahl den Code besonders gut lesbar macht, sollten Sie diese Zahl im Binärformat schreiben:For those times when you're creating bit masks, or whenever a binary representation of a number makes the most readable code, write that number in binary:

public const int Sixteen =   0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;

Das 0b am Anfang der Konstante gibt an, dass die Zahl als binäre Zahl geschrieben wird.The 0b at the beginning of the constant indicates that the number is written as a binary number. Binäre Zahlen können lang werden. Daher kann die Einführung des _ als Zifferntrennzeichen wie in der binären Konstante oben gezeigt die Bitmuster übersichtlicher machen.Binary numbers can get long, so it's often easier to see the bit patterns by introducing the _ as a digit separator, as shown above in the binary constant. Das Zifferntrennzeichen kann überall in der Konstante angezeigt werden.The digit separator can appear anywhere in the constant. Für Zahlen der Basis 10 wird es üblicherweise als Tausendertrennzeichen verwendet:For base 10 numbers, it is common to use it as a thousands separator:

public const long BillionsAndBillions = 100_000_000_000;

Das Zifferntrennzeichen kann auch mit decimal-, float- und double-Typen verwendet werden:The digit separator can be used with decimal, float, and double types as well:

public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;

Insgesamt können Sie numerische Konstanten viel leserlicher deklarieren.Taken together, you can declare numeric constants with much more readability.