Neuerungen von C# 7.0 bis C# 7.3What's new in C# 7.0 through C# 7.3

Von C# 7.0 bis C# 7.3 wurden zahlreiche Features für und inkrementelle Verbesserungen an den Entwicklungsfunktionen von C# eingeführt.C# 7.0 through C# 7.3 brought a number of features and incremental improvements to your development experience with C#. In diesem Artikel erhalten Sie einen Überblick über die neuen Sprachfeatures und Compileroptionen.This article provides an overview of the new language features and compiler options. In den Beschreibungen wird das Verhalten für C# 7.3 erläutert. Dabei handelt es sich um die aktuelle Version, die für auf dem .NET Framework basierende Anwendungen unterstützt wird.The descriptions describe the behavior for C# 7.3, which is the most recent version supported for .NET Framework-based applications.

Das Konfigurationselement für die Sprachversionsauswahl wurde im Rahmen von C# 7.1 hinzugefügt. Damit können Sie die Compilersprachenversion in Ihrer Projektdatei angeben.The language version selection configuration element was added with C# 7.1, which enables you to specify the compiler language version in your project file.

Von C# 7.0 bis C# 7.3 wurden der C#-Sprache die folgenden Features und Designs hinzugefügt:C# 7.0-7.3 adds these features and themes to the C# language:

  • Tupel und VerwerfenTuples and discards
    • 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.
    • 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.
  • async Main-Methodeasync Main method
    • Der Einstiegspunkt für eine Anwendung kann über den Modifizierer async verfügen.The entry point for an application can have the async modifier.
  • 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.
  • defaultLiterale Ausdrückedefault literal expressions
    • Sie können literale Standardausdrücke in Standardwertausdrücken verwenden, wenn der Zieltyp abgeleitet werden kann.You can use default literal expressions in default value expressions when the target type can be inferred.
  • Verbesserung der numerischen literalen SyntaxNumeric literal syntax improvements
    • Neue Token verbessern die Lesbarkeit für numerische Konstanten.New tokens improve readability for numeric constants.
  • 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.
  • Nicht schließende benannte ArgumenteNon-trailing named arguments
    • Positionelle Argumente können auf benannte Argumente folgen.Named arguments can be followed by positional arguments.
  • private protected-Zugriffsmodifiziererprivate protected access modifier
    • Der private protected-Zugriffsmodifizierer ermöglicht den Zugriff für abgeleitete Klassen innerhalb der gleichen Assembly.The private protected access modifier enables access for derived classes in the same assembly.
  • Verbesserte ÜberladungsauflösungImproved overload resolution
    • Neue Regeln sorgen nun für die Auflösung von Ambiguitäten bei Überladungsauflösungen.New rules to resolve overload resolution ambiguity.
  • Techniken zum Schreiben von sicherem, effizientem CodeTechniques for writing safe efficient code
    • Eine Kombination aus Verbesserungen der Syntax, die das Arbeiten mit Werttypen mithilfe von Verweissemantik ermöglichen.A combination of syntax improvements that enable working with value types using reference semantics.

Schließlich verfügt der Compiler über neue Optionen:Finally, the compiler has new options:

  • -refout und -refonly, wodurch die Erstellung von Referenzassemblys gesteuert wird.-refout and -refonly that control reference assembly generation.
  • -publicsign, um das Signieren von Assemblys durch Open Source Software (OSS) zu ermöglichen.-publicsign to enable Open Source Software (OSS) signing of assemblies.
  • -pathmap, um eine Zuordnung für Quellverzeichnisse bereitzustellen.-pathmap to provide a mapping for source directories.

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 sowie die Syntax jedes einzelnen Features kennenlernen.For each feature, you'll learn the reasoning behind it and 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 aus dotnet try.Run dotnet try.

Tupel und VerwerfenTuples and discards

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. C#-Tupeltypen unterstützen == und !=.C# tuple types support == and !=. Weitere Informationen.For more information.

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 run time.

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;

Wenn Sie einen Tupel initialisieren, sind die Variablen, die für die rechte Seite der Zuweisung verwendet werden, oft dieselben, wie die Namen, die Sie den Tupelelementen geben möchten. Die Namen von Tupelelementen können von den Variablen abgeleitet werden, die zum Initialisieren der Tupel verwendet werden:Many times when you initialize a tuple, the variables used for the right side of the assignment are the same as the names you'd like for the tuple elements: The names of tuple elements can be inferred from the variables used to initialize the tuple:

int count = 5;
string label = "Colors used in the map";
var pair = (count, label); // element names are "count" and "label"

Weitere Informationen zu diesem Feature finden Sie im Artikel Tupeltypen.You can learn more about this feature in the Tuple types article.

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

Beim Musterabgleich handelt es sich um mehrere Features, die neue Möglichkeiten eröffnen, Ablaufsteuerung in Ihrem Code auszudrücken.Pattern matching is a set of features that enable new ways to express control flow in your code. Sie können Variablen nach Typ, Werten oder den Werten ihrer Eigenschaften testen.You can test variables for their type, values or the values of their properties. Dieses Vorgehen sorgt für einen besser lesbaren Codeflow.These techniques create more readable code flow.

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.

Ab C# 7.1 kann der Musterausdruck für is und das switch-Typmuster den Typ eines generischen Typparameters haben.Beginning with C# 7.1, the pattern expression for is and the switch type pattern may have the type of a generic type parameter. Dies kann sehr nützlich sein, wenn Sie Typen überprüfen, die entweder struct- oder class-Typen sein können, und Sie Boxing vermeiden möchten.This can be most useful when checking types that may be either struct or class types, and you want to avoid boxing.

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#.

Async MainAsync main

Mithilfe einer async main-Methode können Sie await in Ihrer Main-Methode verwenden.An async main method enables you to use await in your Main method. Vorher hätten Sie das Folgende schreiben müssen:Previously you would need to write:

static int Main()
{
    return DoAsyncWork().GetAwaiter().GetResult();
}

Nun können Sie das Folgende schreiben:You can now write:

static async Task<int> Main()
{
    // This could also be replaced with the body
    // DoAsyncWork, including its await expressions:
    return await DoAsyncWork();
}

Wenn Ihr Programm keinen Exitcode zurückgibt, können Sie eine Main-Methode deklarieren, die einen Task zurückgibt:If your program doesn't return an exit code, you can declare a Main method that returns a Task:

static async Task Main()
{
    await SomeAsyncMethod();
}

Ausführliche Informationen finden Sie unter Async Main im Programmierhandbuch.You can read more about the details in the async main article in the programming guide.

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.";
    }
}

Diese Syntax wird jetzt unterstützt:This syntax is now supported:

[field: SomeThingAboutFieldAttribute]
public int SomeProperty { get; set; }

Das Attribut SomeThingAboutFieldAttribute wird auf das vom Compiler generierte Unterstützungsfeld für SomeProperty angewendet.The attribute SomeThingAboutFieldAttribute is applied to the compiler generated backing field for SomeProperty. Weitere Informationen finden Sie unter attributes im C#-Programmierhandbuch.For more information, see attributes in the C# programming guide.

Hinweis

Einige der Entwürfe, die von lokalen Funktionen unterstützt werden, können auch mithilfe von Lambdaausdrücken erreicht werden.Some of the designs that are supported by local functions can 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 Member mit Ausdruckskörper sind ein wichtiger Meilenstein für die Sprache C#: Diese Features 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.

Literale StandardausdrückeDefault literal expressions

Literale Standardausdrücke sind eine Erweiterung von Ausdrücken mit Standardwert.Default literal expressions are an enhancement to default value expressions. Diese Ausdrücke initialisieren eine Variable auf dem Standardwert.These expressions initialize a variable to the default value. Dort, wo Sie vorher das Folgende geschrieben haben:Where you previously would write:

Func<string, bool> whereClause = default(Func<string, bool>);

Können Sie nun den Typ weglassen, der auf der rechten Seite der Initialisierung steht.You can now omit the type on the right-hand side of the initialization:

Func<string, bool> whereClause = default;

Weitere Informationen finden Sie im Abschnitt Standardliteral des Artikels zum default-Operator.For more information, see the default literal section of the default operator article.

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 von _ als Ziffertrennzeichen wie in der binären Konstante im vorherigen Beispiel 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 in the binary constant in the preceding example. 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. Hexadezimale und binäre numerische Literale dürfen jetzt mit _ beginnen:Hex and binary numeric literals may begin with an _:

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.

out-Variablenout variables

Die vorhandene Syntax zur Unterstützung von out-Parametern wurde in C# 7 verbessert.The existing syntax that supports out parameters has been improved in C# 7. 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");

Sie sollten den Typ der out-Variablen aus Gründen der Übersichtlichkeit angeben. Dies wird im vorherigen Beispiel veranschaulicht.You may want to specify the type of the out variable for clarity, as shown in the preceding example. 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 Codezeile weiter oben.You declare the out variable where you use it, not on a preceding line of code.
  • 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.

Die in C# 7.0 zum Zulassen von out-Variablendeklarationen hinzugefügte Syntax wurde erweitert, sodass sie Feldinitialisierer, Eigenschafteninitialisierer, Konstruktorinitialisierer und Abfrageklauseln umfasst.The syntax added in C# 7.0 to allow out variable declarations has been extended to include field initializers, property initializers, constructor initializers, and query clauses. Sie ermöglicht Code wie im folgenden Beispiel:It enables code such as the following example:

public class B
{
   public B(int i, out int j)
   {
      j = i;
   }
}

public class D : B
{
   public D(int i) : base(i, out var j)
   {
      Console.WriteLine($"The value of 'j' is {j}");
   }
}

Nicht schließende benannte ArgumenteNon-trailing named arguments

Methodenaufrufe dürfen jetzt benannte Argumente verwenden, die positionellen Argumenten vorstehen, wenn diese benannten Argumente in der richtigen Position verwendet werden.Method calls may now use named arguments that precede positional arguments when those named arguments are in the correct positions. Weitere Informationen finden Sie unter Benannte und optionale Argumente (C#-Programmierhandbuch).For more information, see Named and optional arguments.

Zugriffsmodifizierer private protectedprivate protected access modifier

Ein neuer zusammengesetzter Zugriffsmodifizierer: private protected zeigt an, dass auf ein Element durch eine es enthaltende Klasse oder durch innerhalb der gleichen Assembly deklarierte abgeleitete Klassen zugegriffen werden darf.A new compound access modifier: private protected indicates that a member may be accessed by containing class or derived classes that are declared in the same assembly. Während protected internal den Zugriff durch abgeleitete Klassen oder Klassen, die sich in der gleichen Assembly befinden, erlaubt, schränkt private protected den Zugriff auf innerhalb der gleichen Assembly deklarierte abgeleitete Typen ein.While protected internal allows access by derived classes or classes that are in the same assembly, private protected limits access to derived types declared in the same assembly.

Weitere Informationen finden Sie unter Zugriffsmodifizierer (C#-Referenz) in der Sprachreferenz.For more information, see access modifiers in the language reference.

Verbesserte ÜberladungskandidatenImproved overload candidates

In jedem Release werden die Überladungsauflösungsregeln für Situationen aktualisiert, in denen mehrdeutige Methodenaufrufe eine „naheliegende“ Auswahlmöglichkeit haben.In every release, the overload resolution rules get updated to address situations where ambiguous method invocations have an "obvious" choice. In diesem Release werden drei neue Regeln hinzufügt, damit der Compiler die naheliegende Auswahlmöglichkeit nutzt:This release adds three new rules to help the compiler pick the obvious choice:

  1. Wenn eine Methodengruppe sowohl Instanz- als auch statische Member enthält, verwirft der Compiler die Instanzmember, wenn die Methode ohne Instanzempfänger oder -kontext aufgerufen wurde.When a method group contains both instance and static members, the compiler discards the instance members if the method was invoked without an instance receiver or context. Der Compiler verwirft die statischen Member, wenn die Methode mit einem Instanzempfänger aufgerufen wurde.The compiler discards the static members if the method was invoked with an instance receiver. Wenn kein Empfänger vorhanden ist, bezieht der Compiler nur statische Member in einen statischen Kontext ein – andernfalls sowohl statische als auch Instanzmember.When there is no receiver, the compiler includes only static members in a static context, otherwise both static and instance members. Wenn unklar ist, ob der Empfänger eine Instanz oder ein Typ ist, bezieht der Compiler beides ein.When the receiver is ambiguously an instance or type, the compiler includes both. Ein statischer Kontext, wo ein impliziter this-Instanzempfänger nicht verwendet werden kann, enthält den Text von Membern, wo kein this definiert ist, z.B. statische Member, sowie Orte, an denen this nicht verwendet werden kann, z.B. Feld- und Konstruktorinitialisierer.A static context, where an implicit this instance receiver cannot be used, includes the body of members where no this is defined, such as static members, as well as places where this cannot be used, such as field initializers and constructor-initializers.
  2. Wenn eine Methodengruppe einige generische Methoden enthält, deren Typargumente ihre Einschränkungen nicht erfüllen, werden diese Member aus dem Satz von Kandidaten entfernt.When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set.
  3. Für eine Methodengruppenkonvertierung werden Kandidatenmethoden, deren Rückgabetyp nicht mit dem des Delegaten übereinstimmt, aus dem Satz entfernt.For a method group conversion, candidate methods whose return type doesn't match up with the delegate's return type are removed from the set.

Sie werden diese Änderung bemerken, da Sie weniger Compilerfehler für mehrdeutige Methodenüberladungen finden werden, wenn Sie sicher sind, welche Methode besser ist.You'll only notice this change because you'll find fewer compiler errors for ambiguous method overloads when you are sure which method is better.

Ermöglichen von effizienterem sicherem CodeEnabling more efficient safe code

Sie sollten C#-Code sicher schreiben können, der so leistungsstark ist wie unsicherer Code.You should be able to write C# code safely that performs as well as unsafe code. Sicherer Code vermeidet Fehlerklassen, z.B. Pufferüberläufe, verirrte Zeiger und andere Fehler beim Arbeitsspeicherzugriff.Safe code avoids classes of errors, such as buffer overruns, stray pointers, and other memory access errors. Diese neuen Features erweitern die Funktionen des überprüfbaren sicheren Codes.These new features expand the capabilities of verifiable safe code. Schreiben Sie mithilfe sicherer Konstrukte mehr Code.Strive to write more of your code using safe constructs. Diese Features erleichtern dies.These features make that easier.

Die folgenden neuen Features unterstützen das Design der besseren Leistung für sicheren Code:The following new features support the theme of better performance for safe code:

  • Sie können ohne Anheften auf feste Felder zugreifen.You can access fixed fields without pinning.
  • Sie können ref erneut lokale Variablen zuweisen.You can reassign ref local variables.
  • Sie können Initialisierer auf stackalloc-Arrays verwenden.You can use initializers on stackalloc arrays.
  • Sie können fixed-Anweisungen mit jedem Typ verwenden, der ein Muster unterstützt.You can use fixed statements with any type that supports a pattern.
  • Sie können zusätzliche generische Einschränkungen verwenden.You can use additional generic constraints.
  • Den in-Modifizierer für Parameter zur Angabe, dass ein Argument durch Verweis übergeben, von der aufgerufenen Methode aber nicht verändert wird.The in modifier on parameters, to specify that an argument is passed by reference but not modified by the called method. Das Hinzufügen des Modifizierers in zu einem Argument ist eine quellkompatible Änderung.Adding the in modifier to an argument is a source compatible change.
  • Der ref readonly-Modifizierer für die Methodenrückgabe, um anzugeben, dass eine Methode ihren Wert als Verweis zurückgibt und keinen Schreibzugriff auf dieses Objekt zulässt.The ref readonly modifier on method returns, to indicate that a method returns its value by reference but doesn't allow writes to that object. Das Hinzufügen des Modifizierers ref readonly ist eine quellkompatible Änderung, wenn die Rückgabe einem Wert zugewiesen wird.Adding the ref readonly modifier is a source compatible change, if the return is assigned to a value. Das Hinzufügen des Modifizierers readonly zu einer vorhandenen ref-Rückgabeanweisung ist eine inkompatible Änderung.Adding the readonly modifier to an existing ref return statement is an incompatible change. Es verlangt von Aufrufern das Aktualisieren der lokalen ref-Variablen, um den readonly-Modifizierer einzuschließen.It requires callers to update the declaration of ref local variables to include the readonly modifier.
  • Die readonly struct-Deklaration, um anzugeben, dass eine Struktur unveränderlich ist und ihren Membermethoden als in-Parameter übergeben werden sollte.The readonly struct declaration, to indicate that a struct is immutable and should be passed as an in parameter to its member methods. Das Hinzufügen des Modifizierers readonly zu einer vorhandenen Strukturdeklaration ist eine binärkompatible Änderung.Adding the readonly modifier to an existing struct declaration is a binary compatible change.
  • Die ref struct-Deklaration, um anzugeben, dass ein Strukturtyp direkt auf verwalteten Arbeitsspeicher zugreift und immer per Stapel zugeordnet werden muss.The ref struct declaration, to indicate that a struct type accesses managed memory directly and must always be stack allocated. Das Hinzufügen des Modifizierers ref zu einer vorhandenen struct-Deklaration ist eine inkompatible Änderung.Adding the ref modifier to an existing struct declaration is an incompatible change. Ein ref struct kann kein Mitglied einer Klasse sein oder an anderen Stellen verwendet werden, wo es auf dem Heap zugewiesen werden könnte.A ref struct cannot be a member of a class or used in other locations where it may be allocated on the heap.

Weitere Informationen zu all diesen Änderungen finden Sie unter Schreiben von sicherem und effizientem Code.You can read more about all these changes in Write safe efficient code.

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 return-Anweisungen einer Methode das ref-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.

Lokale ref-Variablen werden jetzt möglicherweise neu zugewiesen, um nach der Initialisierung auf verschiedene Instanzen zu verweisen.Now, ref locals may be reassigned to refer to different instances after being initialized. Der folgende Code wird jetzt kompiliert:The following code now compiles:

ref VeryLargeStruct refLocal = ref veryLargeStruct; // initialization
refLocal = ref anotherVeryLargeStruct; // reassigned, refLocal refers to different storage.

Weitere Informationen finden Sie im Artikel zu ref-Rückgaben und lokalen ref-Variablen und im Artikel zu foreach.For more information, see the article on ref returns and ref locals, and the article on foreach.

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

Bedingte ref AusdrückeConditional ref expressions

Schließlich kann ein bedingter Ausdruck als Ergebnis einen Verweis anstatt eines Werts erzeugen.Finally, the conditional expression may produce a ref result instead of a value result. Beispielsweise würden Sie folgendes schreiben, um einen Verweis auf das erste Element in einem von zwei Arrays abzurufen:For example, you would write the following to retrieve a reference to the first element in one of two arrays:

ref var r = ref (arr != null ? ref arr[0] : ref otherArr[0]);

Die Variable r ist ein Verweis auf den ersten Wert in arr oder otherArr.The variable r is a reference to the first value in either arr or otherArr.

Weitere Informationen finden Sie unter conditional-Operator (?:) in der Sprachreferenz.For more information, see conditional operator (?:) in the language reference.

Modifizierer für in-Parameterin parameter modifier

Das in-Schlüsselwort ergänzt die vorhandenen Schlüsselwörter ref und out zum Übergeben von Argumenten als Verweis.The in keyword complements the existing ref and out keywords to pass arguments by reference. Durch das in-Schlüsselwort wird festgelegt, dass das Argument als Verweis übergeben wird, die aufgerufene Methode aber nicht den Wert ändert.The in keyword specifies passing the argument by reference, but the called method doesn't modify the value.

Sie können Überladungen, die nach Wert oder nach schreibgeschütztem Verweis übergeben werden, wie im folgenden Code gezeigt deklarieren:You may declare overloads that pass by value or by readonly reference, as shown in the following code:

static void M(S arg);
static void M(in S arg);

Die Überladung, die nach Wert übergeben wird (die erste im obigen Beispiel) ist besser als die Version, die nach schreibgeschütztem Verweis übergeben wird.The by value (first in the preceding example) overload is better than the by readonly reference version. Um die Version mit dem readonly-Verweisargument aufzurufen, müssen Sie auch den in-Modifizierer beim Aufrufen der Methode einbeziehen.To call the version with the readonly reference argument, you must include the in modifier when calling the method.

Weitere Informationen finden Sie im Artikel zum in-Parametermodifizierer.For more information, see the article on the in parameter modifier.

Weitere Typen unterstützen die fixed-AnweisungMore types support the fixed statement

Die fixed-Anweisung unterstützte eine begrenzte Anzahl von Typen.The fixed statement supported a limited set of types. Ab C# 7.3 kann jeder Typ, der eine GetPinnableReference()-Methode enthält, die eine ref T oder ref readonly T zurückgibt, fixed sein.Starting with C# 7.3, any type that contains a GetPinnableReference() method that returns a ref T or ref readonly T may be fixed. Dieses Feature hinzuzufügen, bedeutet, dass fixed mit System.Span<T> und verwandten Typen genutzt werden kann.Adding this feature means that fixed can be used with System.Span<T> and related types.

Weitere Informationen finden Sie im Artikel zur fixed-Anweisung in der Sprachreferenz.For more information, see the fixed statement article in the language reference.

Indizieren von fixed-Feldern erfordert kein AnheftenIndexing fixed fields does not require pinning

Betrachten Sie diese Struktur:Consider this struct:

unsafe struct S
{
    public fixed int myFixedField[10];
}

In früheren Versionen von C# mussten Sie eine Variable für den Zugriff auf eine der ganzen Zahlen anheften, die Teil von myFixedField sind.In earlier versions of C#, you needed to pin a variable to access one of the integers that are part of myFixedField. Der folgende Code wird kompiliert, ohne die Variable p in einer separaten fixed-Anweisung anzuheften:Now, the following code compiles without pinning the variable p inside a separate fixed statement:

class C
{
    static S s = new S();

    unsafe public void M()
    {
        int p = s.myFixedField[5];
    }
}

Die Variable p greift auf ein Element in myFixedField zu.The variable p accesses one element in myFixedField. Sie müssen keine separate int*-Variable deklarieren.You don't need to declare a separate int* variable. Sie benötigen weiterhin einen unsafe-Kontext.You still need an unsafe context. In früheren Versionen von C# müssen Sie einen zweiten festen Zeiger deklarieren:In earlier versions of C#, you need to declare a second fixed pointer:

class C
{
    static S s = new S();

    unsafe public void M()
    {
        fixed (int* ptr = s.myFixedField)
        {
            int p = ptr[5];
        }
    }
}

Weitere Informationen finden Sie im Artikel zur fixed-Anweisung.For more information, see the article on the fixed statement.

stackalloc-Arrays unterstützen Initialisiererstackalloc arrays support initializers

Sie konnten schon die Werte für Elemente in einem Array angeben, wenn Sie es initialisierten:You've been able to specify the values for elements in an array when you initialize it:

var arr = new int[3] {1, 2, 3};
var arr2 = new int[] {1, 2, 3};

Nun kann die gleiche Syntax auf Arrays angewendet werden, die mit stackalloc deklariert werden:Now, that same syntax can be applied to arrays that are declared with stackalloc:

int* pArr = stackalloc int[3] {1, 2, 3};
int* pArr2 = stackalloc int[] {1, 2, 3};
Span<int> arr = stackalloc [] {1, 2, 3};

Weitere Informationen finden Sie im Artikel zum stackalloc-Operator.For more information, see the stackalloc operator article.

Verbesserte generische EinschränkungenEnhanced generic constraints

Sie können jetzt Typ System.Enum oder System.Delegate als Basisklasseneinschränkungen für einen Typparameter angeben.You can now specify the type System.Enum or System.Delegate as base class constraints for a type parameter.

Sie können auch die neue unmanaged-Einschränkung nutzen, um anzugeben, dass der Typparameter ein nicht verwalteter Non-Nullable-Typ sein muss.You can also use the new unmanaged constraint, to specify that a type parameter must be a non-nullable unmanaged type.

Weitere Informationen finden Sie in den Artikeln zu generischen where-Einschränkungen und Einschränkungen für Typparameter.For more information, see the articles on where generic constraints and constraints on type parameters.

Das Hinzufügen dieser Einschränkungen zu vorhandenen Typen ist eine inkompatible Änderung.Adding these constraints to existing types is an incompatible change. Geschlossene generische Typen erfüllen diese neuen Einschränkungen möglicherweise nicht mehr.Closed generic types may no longer meet these new constraints.

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 System.Threading.Tasks.Extensions hinzufügen, 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.

Neue CompileroptionenNew compiler options

Neue Compileroptionen unterstützen neue Build- und DevOpsszenarien für C#-Programme.New compiler options support new build and DevOps scenarios for C# programs.

Generierung der ReferenzassemblyReference assembly generation

Es gibt zwei neue Compileroptionen, die reine Verweisassemblys generieren: ProduceReferenceAssembly und ProduceOnlyReferenceAssembly.There are two new compiler options that generate reference-only assemblies: ProduceReferenceAssembly and ProduceOnlyReferenceAssembly. In den verlinkten Artikeln werden diese Optionen und Referenzassemblys ausführlich beschrieben.The linked articles explain these options and reference assemblies in more detail.

Öffentliche oder Open Source-SignierungPublic or Open Source signing

Die Compileroption PublicSign weist den Compiler an, die Assembly mit einem öffentlichen Schlüssel zu signieren.The PublicSign compiler option instructs the compiler to sign the assembly using a public key. Die Assembly wird als signiert markiert, aber die Signatur wird dem öffentlichen Schlüssel entnommen.The assembly is marked as signed, but the signature is taken from the public key. Mit dieser Option können Sie signierte Assemblys aus Open Source-Projekten mit einem öffentlichen Schlüssel erstellen.This option enables you to build signed assemblies from open-source projects using a public key.

Weitere Informationen finden Sie im Artikel zur PublicSign-Compileroption.For more information, see the PublicSign compiler option article.

pathmappathmap

Die Compileroption PathMap weist den Compiler an, Quellpfade aus der Buildumgebung mit zugeordneten Quellpfaden zu ersetzen.The PathMap compiler option instructs the compiler to replace source paths from the build environment with mapped source paths. Die Option PathMapCallerFilePathAttribute steuert den Quellpfad, der vom Compiler in PDB-Dateien oder für geschrieben wird.The PathMap option controls the source path written by the compiler to PDB files or for the CallerFilePathAttribute.

Weitere Informationen finden Sie im Artikel zur PathMap-Compileroption.For more information, see the PathMap compiler option article.