Co je nového v jazyce C# 8.0What's new in C# 8.0

C# 8,0 přidává následující funkce a vylepšení jazyka C#:C# 8.0 adds the following features and enhancements to the C# language:

C# 8,0 se podporuje v .NET Core 3. x a .NET Standard 2,1.C# 8.0 is supported on .NET Core 3.x and .NET Standard 2.1. Další informace najdete v tématu Správa verzí jazyka C#.For more information, see C# language versioning.

Zbývající část tohoto článku stručně popisuje tyto funkce.The remainder of this article briefly describes these features. Kde jsou k dispozici podrobné články, jsou uvedeny odkazy na tyto kurzy a přehledy.Where in-depth articles are available, links to those tutorials and overviews are provided. Pomocí globálního nástroje můžete prozkoumat tyto funkce ve vašem prostředí dotnet try :You can explore these features in your environment using the dotnet try global tool:

  1. Nainstalujte nástroj dotnet-try Global.Install the dotnet-try global tool.
  2. Naklonujte úložiště dotnet/try-Samples .Clone the dotnet/try-samples repository.
  3. Nastavte aktuální adresář do podadresáře csharp8 pro úložiště Try-Samples .Set the current directory to the csharp8 subdirectory for the try-samples repository.
  4. Spusťte dotnet try.Run dotnet try.

Členové jen pro čteníReadonly members

Můžete použít readonly modifikátor u členů struktury.You can apply the readonly modifier to members of a struct. Indikuje, že člen nemění stav.It indicates that the member doesn't modify state. Je lépe podrobnější než použití readonly modifikátoru na struct deklaraci.It's more granular than applying the readonly modifier to a struct declaration. Vezměte v úvahu následující proměnlivou strukturu:Consider the following mutable struct:

public struct Point
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Distance => Math.Sqrt(X * X + Y * Y);

    public override string ToString() =>
        $"({X}, {Y}) is {Distance} from the origin";
}

Podobně jako u většiny struktur ToString() Metoda nemění stav.Like most structs, the ToString() method doesn't modify state. Můžete určit, že přidáním readonly modifikátoru k deklaraci ToString() :You could indicate that by adding the readonly modifier to the declaration of ToString():

public readonly override string ToString() =>
    $"({X}, {Y}) is {Distance} from the origin";

Předchozí změna vygeneruje upozornění kompilátoru, protože ToString přistupuje k Distance vlastnosti, která není označena jako readonly :The preceding change generates a compiler warning, because ToString accesses the Distance property, which isn't marked readonly:

warning CS8656: Call to non-readonly member 'Point.Distance.get' from a 'readonly' member results in an implicit copy of 'this'

Kompilátor vás upozorní, když potřebuje vytvořit obrannou linií kopii.The compiler warns you when it needs to create a defensive copy. DistanceVlastnost nemění stav, takže můžete toto upozornění opravit přidáním readonly modifikátoru k deklaraci:The Distance property doesn't change state, so you can fix this warning by adding the readonly modifier to the declaration:

public readonly double Distance => Math.Sqrt(X * X + Y * Y);

Všimněte si, že readonly Modifikátor je nutný pro vlastnost, která je jen pro čtení.Notice that the readonly modifier is necessary on a read-only property. Kompilátor nepředpokládá get , že přistupující objekty nemění stav. musíte deklarovat readonly explicitně.The compiler doesn't assume get accessors don't modify state; you must declare readonly explicitly. Automaticky implementované vlastnosti představují výjimku. Kompilátor bude zacházet se všemi automaticky implementovanými metodami getter readonly , takže zde není nutné přidávat readonly modifikátor do X Y vlastností a.Auto-implemented properties are an exception; the compiler will treat all auto-implemented getters as readonly, so here there's no need to add the readonly modifier to the X and Y properties.

Kompilátor vynutil pravidlo, že readonly Členové nemění stav.The compiler does enforce the rule that readonly members don't modify state. Následující metoda nebude zkompilována, dokud neodeberete readonly Modifikátor:The following method won't compile unless you remove the readonly modifier:

public readonly void Translate(int xOffset, int yOffset)
{
    X += xOffset;
    Y += yOffset;
}

Tato funkce umožňuje určit záměr návrhu, aby ho kompilátor mohl vynutit a na základě tohoto záměru provádět optimalizace.This feature lets you specify your design intent so the compiler can enforce it, and make optimizations based on that intent.

Další informace naleznete v oddílu readonly instance členů článku typy struktury .For more information, see the readonly instance members section of the Structure types article.

Výchozí metody rozhraníDefault interface methods

Nyní můžete přidat členy do rozhraní a poskytnout implementaci pro tyto členy.You can now add members to interfaces and provide an implementation for those members. Tato funkce jazyka umožňuje autorům rozhraní API přidávat metody do rozhraní v pozdějších verzích bez narušení zdrojové nebo binární kompatibility se stávajícími implementacemi tohoto rozhraní.This language feature enables API authors to add methods to an interface in later versions without breaking source or binary compatibility with existing implementations of that interface. Stávající implementace dědí výchozí implementaci.Existing implementations inherit the default implementation. Tato funkce také umožňuje jazyku C# spolupracovat s rozhraními API, která cílí na Android nebo SWIFT, což podporuje podobné funkce.This feature also enables C# to interoperate with APIs that target Android or Swift, which support similar features. Výchozí metody rozhraní také umožňují scénáře podobně jako funkce jazyka "vlastnosti".Default interface methods also enable scenarios similar to a "traits" language feature.

Výchozí metody rozhraní ovlivňují mnoho scénářů a prvků jazyka.Default interface methods affect many scenarios and language elements. Náš první kurz popisuje aktualizaci rozhraní s výchozími implementacemi.Our first tutorial covers updating an interface with default implementations. Další kurzy a referenční aktualizace jsou k disčase pro obecné vydání.Other tutorials and reference updates are coming in time for general release.

Další vzory na více místechMore patterns in more places

Porovnávání vzorů poskytuje nástrojům poskytování funkcionality závislé na tvaru v rámci souvisejících, ale různých druhů dat.Pattern matching gives tools to provide shape-dependent functionality across related but different kinds of data. C# 7,0 představil Syntax pro vzory typů a konstantní vzorce pomocí is výrazu a switch příkazu.C# 7.0 introduced syntax for type patterns and constant patterns by using the is expression and the switch statement. Tyto funkce představovaly první nezávazné kroky směrem k podpoře programovacích paradigmat, ve kterých jsou data a funkce živě oddělené.These features represented the first tentative steps toward supporting programming paradigms where data and functionality live apart. Vzhledem k tomu, že se odvětví pohybuje k většímu mikroslužbám a dalším cloudovým architekturám, jsou potřeba jiné jazykové nástroje.As the industry moves toward more microservices and other cloud-based architectures, other language tools are needed.

C# 8,0 rozšiřuje tento slovník, takže můžete použít více výrazů vzoru ve více místech kódu.C# 8.0 expands this vocabulary so you can use more pattern expressions in more places in your code. Tyto funkce zvažte, když jsou vaše data a funkce oddělené.Consider these features when your data and functionality are separate. Zvažte porovnávání vzorů, pokud jsou algoritmy závislé na jiném faktě, než je typ modulu runtime objektu.Consider pattern matching when your algorithms depend on a fact other than the runtime type of an object. Tyto techniky poskytují jiný způsob, jak vyjádřit návrhy.These techniques provide another way to express designs.

Kromě nových vzorů na nových místech C# 8,0 přidává rekurzivní vzory.In addition to new patterns in new places, C# 8.0 adds recursive patterns. Výsledek jakéhokoli výrazu vzoru je výraz.The result of any pattern expression is an expression. Rekurzivní vzorek je pouze výraz vzoru aplikovaný na výstup jiného výrazu vzoru.A recursive pattern is simply a pattern expression applied to the output of another pattern expression.

Výrazy SwitchSwitch expressions

switchPříkaz často v každém z jeho bloků vytvoří hodnotu case .Often, a switch statement produces a value in each of its case blocks. Výrazy Switch umožňují použít stručnější syntaxi výrazů.Switch expressions enable you to use more concise expression syntax. K dispozici jsou méně opakující se case a break klíčová slova a méně složené závorky.There are fewer repetitive case and break keywords, and fewer curly braces. Podívejte se například na následující výčet, který obsahuje seznam barev duha:As an example, consider the following enum that lists the colors of the rainbow:

public enum Rainbow
{
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Indigo,
    Violet
}

Pokud vaše aplikace definovala RGBColor typ, který je vytvořen z rozhraní R , G a B , mohli byste převést Rainbow hodnotu na její hodnoty RGB pomocí následující metody, která obsahuje výraz Switch:If your application defined an RGBColor type that is constructed from the R, G and B components, you could convert a Rainbow value to its RGB values using the following method containing a switch expression:

public static RGBColor FromRainbow(Rainbow colorBand) =>
    colorBand switch
    {
        Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
        Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
        Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
        Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
        Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
        Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
        Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
        _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
    };

Tady je několik vylepšení syntaxe:There are several syntax improvements here:

  • Proměnná se nachází před switch klíčovým slovem.The variable comes before the switch keyword. V jiném pořadí je vizuálně snadné odlišit výraz přepínače od příkazu switch.The different order makes it visually easy to distinguish the switch expression from the switch statement.
  • casePrvky a : jsou nahrazeny => .The case and : elements are replaced with =>. Je výstižnější a intuitivní.It's more concise and intuitive.
  • defaultPřípad je nahrazen _ zahozením.The default case is replaced with a _ discard.
  • Tělo jsou výrazy, nikoli příkazy.The bodies are expressions, not statements.

Kontrast se stejným kódem pomocí switch příkazu Classic:Contrast that with the equivalent code using the classic switch statement:

public static RGBColor FromRainbowClassic(Rainbow colorBand)
{
    switch (colorBand)
    {
        case Rainbow.Red:
            return new RGBColor(0xFF, 0x00, 0x00);
        case Rainbow.Orange:
            return new RGBColor(0xFF, 0x7F, 0x00);
        case Rainbow.Yellow:
            return new RGBColor(0xFF, 0xFF, 0x00);
        case Rainbow.Green:
            return new RGBColor(0x00, 0xFF, 0x00);
        case Rainbow.Blue:
            return new RGBColor(0x00, 0x00, 0xFF);
        case Rainbow.Indigo:
            return new RGBColor(0x4B, 0x00, 0x82);
        case Rainbow.Violet:
            return new RGBColor(0x94, 0x00, 0xD3);
        default:
            throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand));
    };
}

Vzory vlastnostíProperty patterns

Vzor vlastnosti umožňuje porovnávat vlastnosti objektu, který je zkontrolován.The property pattern enables you to match on properties of the object examined. Vezměte v úvahu web elektronického obchodování, který musí počítat DPH na základě adresy kupujícího.Consider an eCommerce site that must compute sales tax based on the buyer's address. Tento výpočet není základní zodpovědností Address třídy.That computation isn't a core responsibility of an Address class. Změní se v průběhu času, nejspíš častěji než změny formátu adresy.It will change over time, likely more often than address format changes. Částka DPH závisí na State vlastnosti adresy.The amount of sales tax depends on the State property of the address. Následující metoda používá vzorek vlastností k výpočtu DPH z adresy a ceny:The following method uses the property pattern to compute the sales tax from the address and the price:

public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
    location switch
    {
        { State: "WA" } => salePrice * 0.06M,
        { State: "MN" } => salePrice * 0.075M,
        { State: "MI" } => salePrice * 0.05M,
        // other cases removed for brevity...
        _ => 0M
    };

Porovnávání vzorů vytvoří stručnou syntaxi pro vyjádření tohoto algoritmu.Pattern matching creates a concise syntax for expressing this algorithm.

Vzory řazené kolekce členůTuple patterns

Některé algoritmy závisí na více vstupech.Some algorithms depend on multiple inputs. Vzorce řazené kolekce členů umožňují přepínat na základě více hodnot vyjádřených jako řazené kolekce členů.Tuple patterns allow you to switch based on multiple values expressed as a tuple. Následující kód ukazuje výraz přepínače pro herní rock, papír, nůžky:The following code shows a switch expression for the game rock, paper, scissors:

public static string RockPaperScissors(string first, string second)
    => (first, second) switch
    {
        ("rock", "paper") => "rock is covered by paper. Paper wins.",
        ("rock", "scissors") => "rock breaks scissors. Rock wins.",
        ("paper", "rock") => "paper covers rock. Paper wins.",
        ("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
        ("scissors", "rock") => "scissors is broken by rock. Rock wins.",
        ("scissors", "paper") => "scissors cuts paper. Scissors wins.",
        (_, _) => "tie"
    };

Zprávy indikují vítěze.The messages indicate the winner. Případ zahození představuje tři kombinace pro vazby nebo jiné textové vstupy.The discard case represents the three combinations for ties, or other text inputs.

Poziční vzoryPositional patterns

Některé typy obsahují Deconstruct metodu, která dekonstruuje své vlastnosti do diskrétních proměnných.Some types include a Deconstruct method that deconstructs its properties into discrete variables. Když Deconstruct je metoda přístupná, můžete použít poziční vzory pro kontrolu vlastností objektu a použít tyto vlastnosti pro vzor.When a Deconstruct method is accessible, you can use positional patterns to inspect properties of the object and use those properties for a pattern. Vezměte v úvahu následující Point třídu, která obsahuje Deconstruct metodu pro vytvoření diskrétních proměnných pro X a Y :Consider the following Point class that includes a Deconstruct method to create discrete variables for X and Y:

public class Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y) => (X, Y) = (x, y);

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

Navíc zvažte následující výčet, který představuje různé pozice kvadrantu:Additionally, consider the following enum that represents various positions of a quadrant:

public enum Quadrant
{
    Unknown,
    Origin,
    One,
    Two,
    Three,
    Four,
    OnBorder
}

Následující metoda používá poziční vzor k extrakci hodnot x a y .The following method uses the positional pattern to extract the values of x and y. Pak pomocí when klauzule určíte Quadrant bod:Then, it uses a when clause to determine the Quadrant of the point:

static Quadrant GetQuadrant(Point point) => point switch
{
    (0, 0) => Quadrant.Origin,
    var (x, y) when x > 0 && y > 0 => Quadrant.One,
    var (x, y) when x < 0 && y > 0 => Quadrant.Two,
    var (x, y) when x < 0 && y < 0 => Quadrant.Three,
    var (x, y) when x > 0 && y < 0 => Quadrant.Four,
    var (_, _) => Quadrant.OnBorder,
    _ => Quadrant.Unknown
};

Vzor zahození v předchozím přepínači odpovídá, pokud buď x nebo y je 0, ale ne obojí.The discard pattern in the preceding switch matches when either x or y is 0, but not both. Výraz přepínače musí buď vytvořit hodnotu, nebo vyvolat výjimku.A switch expression must either produce a value or throw an exception. Pokud žádný z případů neodpovídá, výraz přepínače vyvolá výjimku.If none of the cases match, the switch expression throws an exception. Kompilátor vygeneruje upozornění, pokud nepokrýváte všechny možné případy ve výrazu přepínače.The compiler generates a warning for you if you don't cover all possible cases in your switch expression.

Techniky porovnávání vzorů můžete prozkoumat v tomto pokročilém kurzu o porovnávání vzorů.You can explore pattern matching techniques in this advanced tutorial on pattern matching.

Používání deklaracíUsing declarations

Deklarace using je deklarace proměnné před using klíčovým slovem.A using declaration is a variable declaration preceded by the using keyword. Dává kompilátoru pokyn, že proměnná, která má být deklarována, by měla být uvolněna na konci ohraničujícího oboru.It tells the compiler that the variable being declared should be disposed at the end of the enclosing scope. Zvažte například následující kód, který zapisuje textový soubor:For example, consider the following code that writes a text file:

static int WriteLinesToFile(IEnumerable<string> lines)
{
    using var file = new System.IO.StreamWriter("WriteLines2.txt");
    // Notice how we declare skippedLines after the using statement.
    int skippedLines = 0;
    foreach (string line in lines)
    {
        if (!line.Contains("Second"))
        {
            file.WriteLine(line);
        }
        else
        {
            skippedLines++;
        }
    }
    // Notice how skippedLines is in scope here.
    return skippedLines;
    // file is disposed here
}

V předchozím příkladu je soubor zlikvidován při dosažení uzavírací závorky pro metodu.In the preceding example, the file is disposed when the closing brace for the method is reached. To je konec oboru, ve kterém file je deklarováno.That's the end of the scope in which file is declared. Předchozí kód je ekvivalentní následujícímu kódu, který používá příkaz Classic using:The preceding code is equivalent to the following code that uses the classic using statement:

static int WriteLinesToFile(IEnumerable<string> lines)
{
    // We must declare the variable outside of the using block
    // so that it is in scope to be returned.
    int skippedLines = 0;
    using (var file = new System.IO.StreamWriter("WriteLines2.txt"))
    {
        foreach (string line in lines)
        {
            if (!line.Contains("Second"))
            {
                file.WriteLine(line);
            }
            else
            {
                skippedLines++;
            }
        }
        return skippedLines;
    } // file is disposed here
}

V předchozím příkladu je soubor zlikvidován při dosažení uzavírací závorky přidružené k using příkazu.In the preceding example, the file is disposed when the closing brace associated with the using statement is reached.

V obou případech kompilátor vygeneruje volání Dispose() .In both cases, the compiler generates the call to Dispose(). Kompilátor vygeneruje chybu, pokud výraz v using příkazu není jednorázově.The compiler generates an error if the expression in the using statement isn't disposable.

Statické místní funkceStatic local functions

Nyní můžete přidat static Modifikátor k místním funkcím, aby se zajistilo, že lokální funkce nebude zachytit (odkaz) jakékoli proměnné z ohraničujícího oboru.You can now add the static modifier to local functions to ensure that local function doesn't capture (reference) any variables from the enclosing scope. Tím se vygeneruje CS8421 "statická místní funkce nemůže obsahovat odkaz na <variable> ."Doing so generates CS8421, "A static local function can't contain a reference to <variable>."

Vezměte v úvahu následující kód.Consider the following code. Místní funkce LocalFunction přistupuje k proměnné y deklarované v ohraničujícím oboru (metoda M ).The local function LocalFunction accesses the variable y, declared in the enclosing scope (the method M). Proto LocalFunction nelze deklarovat s static modifikátorem:Therefore, LocalFunction can't be declared with the static modifier:

int M()
{
    int y;
    LocalFunction();
    return y;

    void LocalFunction() => y = 0;
}

Následující kód obsahuje statickou místní funkci.The following code contains a static local function. Může být statický, protože nemá přístup k žádným proměnným v ohraničujícím oboru:It can be static because it doesn't access any variables in the enclosing scope:

int M()
{
    int y = 5;
    int x = 7;
    return Add(x, y);

    static int Add(int left, int right) => left + right;
}

Struktury odkazů na jedno použitíDisposable ref structs

structDeklarace s ref modifikátorem nesmí implementovat žádná rozhraní, a proto nemůže implementovat IDisposable .A struct declared with the ref modifier may not implement any interfaces and so can't implement IDisposable. Proto aby bylo možné ref struct Odstranit, musí mít přístupnou void Dispose() metodu.Therefore, to enable a ref struct to be disposed, it must have an accessible void Dispose() method. Tato funkce se vztahuje také na readonly ref struct deklarace.This feature also applies to readonly ref struct declarations.

Odkazové typy s možnou hodnotou nullNullable reference types

V kontextu anotace s možnou hodnotou null je jakákoli proměnná typu odkazu považována za typ odkazu, který není null.Inside a nullable annotation context, any variable of a reference type is considered to be a nonnullable reference type. Pokud chcete označit, že proměnná může mít hodnotu null, je nutné připojit název typu ? k deklaraci proměnné jako typ odkazu s možnou hodnotou null.If you want to indicate that a variable may be null, you must append the type name with the ? to declare the variable as a nullable reference type.

Pro nehodnotový odkazový typ kompilátor používá analýzu toků k zajištění, že lokální proměnné jsou inicializovány na hodnotu, která není null, je-li deklarována.For nonnullable reference types, the compiler uses flow analysis to ensure that local variables are initialized to a non-null value when declared. Pole musí být inicializována během konstrukce.Fields must be initialized during construction. Kompilátor vygeneruje upozornění, pokud proměnná není nastavena voláním žádné z dostupných konstruktorů nebo inicializátoru.The compiler generates a warning if the variable isn't set by a call to any of the available constructors or by an initializer. Kromě toho nemůžete přiřadit typy odkazů, které mohou mít hodnotu null.Furthermore, nonnullable reference types can't be assigned a value that could be null.

Typy odkazů s možnou hodnotou null nejsou kontrolovány, aby se zajistilo, že nejsou přiřazeny nebo inicializoványNullable reference types aren't checked to ensure they aren't assigned or initialized to null. Kompilátor však používá analýzu toků k zajištění toho, aby byla jakákoli proměnná typu odkazu s možnou hodnotou null zkontrolována před tím, než bude k dispozici nebo přiřazena k neprázdnému typu odkazu.However, the compiler uses flow analysis to ensure that any variable of a nullable reference type is checked against null before it's accessed or assigned to a nonnullable reference type.

Další informace o této funkci najdete v přehledu typů odkazů s možnou hodnotou null.You can learn more about the feature in the overview of nullable reference types. Zkuste to sami v nové aplikaci v tomto kurzu odkazové typy s možnou hodnotou null.Try it yourself in a new application in this nullable reference types tutorial. Přečtěte si o krocích k migraci existujícího základu kódu, aby bylo možné používat v migraci aplikace na použití typů odkazů s možnou hodnotou null v kurzu.Learn about the steps to migrate an existing codebase to make use of nullable reference types in the migrating an application to use nullable reference types tutorial.

Asynchronní proudyAsynchronous streams

Počínaje jazykem C# 8,0 můžete vytvářet a spotřebovávat streamy asynchronně.Starting with C# 8.0, you can create and consume streams asynchronously. Metoda, která vrací asynchronní datový proud má tři vlastnosti:A method that returns an asynchronous stream has three properties:

  1. Je deklarována s async modifikátorem.It's declared with the async modifier.
  2. Vrátí IAsyncEnumerable<T> .It returns an IAsyncEnumerable<T>.
  3. Metoda obsahuje yield return příkazy pro vrácení po sobě jdoucích prvků v asynchronním datovém proudu.The method contains yield return statements to return successive elements in the asynchronous stream.

Spotřebovávání asynchronního datového proudu vyžaduje, abyste await před foreach klíčovým slovem při vytváření výčtu prvků v datovém proudu přidali klíčové slovo.Consuming an asynchronous stream requires you to add the await keyword before the foreach keyword when you enumerate the elements of the stream. Přidání await klíčového slova vyžaduje metodu, která vytvoří výčet asynchronního datového proudu, který má být deklarován s async modifikátorem a vrátí typ povolený pro async metodu.Adding the await keyword requires the method that enumerates the asynchronous stream to be declared with the async modifier and to return a type allowed for an async method. Obvykle to znamená, že vrací Task nebo Task<TResult> .Typically that means returning a Task or Task<TResult>. Může to být také ValueTask nebo ValueTask<TResult> .It can also be a ValueTask or ValueTask<TResult>. Metoda může spotřebovávat a vytvořit asynchronní datový proud, což znamená, že by vrátilo IAsyncEnumerable<T> .A method can both consume and produce an asynchronous stream, which means it would return an IAsyncEnumerable<T>. Následující kód vygeneruje sekvenci od 0 do 19, čekání 100 MS mezi vygenerováním každého čísla:The following code generates a sequence from 0 to 19, waiting 100 ms between generating each number:

public static async System.Collections.Generic.IAsyncEnumerable<int> GenerateSequence()
{
    for (int i = 0; i < 20; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}

Vyčíslení sekvence pomocí await foreach příkazu:You would enumerate the sequence using the await foreach statement:

await foreach (var number in GenerateSequence())
{
    Console.WriteLine(number);
}

Asynchronní streamy si můžete vyzkoušet sami v našem kurzu vytváření a využívání asynchronních datových proudů.You can try asynchronous streams yourself in our tutorial on creating and consuming async streams. Ve výchozím nastavení jsou prvky Stream zpracovávány v zachyceném kontextu.By default, stream elements are processed in the captured context. Pokud chcete zakázat zachycování kontextu, použijte TaskAsyncEnumerableExtensions.ConfigureAwait metodu rozšíření.If you want to disable capturing of the context, use the TaskAsyncEnumerableExtensions.ConfigureAwait extension method. Další informace o kontextech synchronizace a zaznamenávání aktuálního kontextu naleznete v článku o využívání asynchronního vzoru založeného na úlohách.For more information about synchronization contexts and capturing the current context, see the article on consuming the Task-based asynchronous pattern.

Asynchronní na jedno použitíAsynchronous disposable

Počínaje jazykem C# 8,0 podporuje jazyk asynchronní typy v případě, které implementují System.IAsyncDisposable rozhraní.Starting with C# 8.0, the language supports asynchronous disposable types that implement the System.IAsyncDisposable interface. Použijete await using příkaz pro práci s asynchronním objektem na jedno.You use the await using statement to work with an asynchronously disposable object. Další informace najdete v článku implementace metody DisposeAsync .For more information, see the Implement a DisposeAsync method article.

Indexy a rozsahyIndices and ranges

Indexy a rozsahy poskytují stručnou syntaxi pro přístup k jednotlivým prvkům nebo rozsahům v sekvenci.Indices and ranges provide a succinct syntax for accessing single elements or ranges in a sequence.

Tato podpora jazyků spoléhá na dva nové typy a dva nové operátory:This language support relies on two new types, and two new operators:

  • System.Index představuje index do sekvence.System.Index represents an index into a sequence.
  • Index z operátoru End ^ , který určuje, že index je relativní ke konci sekvence.The index from end operator ^, which specifies that an index is relative to the end of the sequence.
  • System.Range představuje dílčí rozsah sekvence.System.Range represents a sub range of a sequence.
  • Operátor rozsahu .. , který určuje začátek a konec rozsahu jako jeho operandy.The range operator .., which specifies the start and end of a range as its operands.

Pojďme začít s pravidly pro indexy.Let's start with the rules for indexes. Zvažte pole sequence .Consider an array sequence. 0Index je stejný jako sequence[0] .The 0 index is the same as sequence[0]. ^0Index je stejný jako sequence[sequence.Length] .The ^0 index is the same as sequence[sequence.Length]. Všimněte si, že sequence[^0] vyvolá výjimku, stejně jako sequence[sequence.Length] .Note that sequence[^0] does throw an exception, just as sequence[sequence.Length] does. Pro jakékoli číslo n ^n je index stejný jako sequence.Length - n .For any number n, the index ^n is the same as sequence.Length - n.

Rozsah Určuje začátek a konec rozsahu.A range specifies the start and end of a range. Začátek rozsahu je včetně, ale konec rozsahu je exkluzivní, což znamená, že začátek je zahrnutý v rozsahu, ale konec není zahrnutý v rozsahu.The start of the range is inclusive, but the end of the range is exclusive, meaning the start is included in the range but the end isn't included in the range. Rozsah [0..^0] představuje celý rozsah, stejně jako [0..sequence.Length] představuje celý rozsah.The range [0..^0] represents the entire range, just as [0..sequence.Length] represents the entire range.

Podívejme se na několik příkladů.Let's look at a few examples. Vezměte v úvahu následující pole s poznámkou s jeho indexem od začátku do konce:Consider the following array, annotated with its index from the start and from the end:

var words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

Poslední slovo můžete načíst s ^1 indexem:You can retrieve the last word with the ^1 index:

Console.WriteLine($"The last word is {words[^1]}");
// writes "dog"

Následující kód vytvoří dílčí rozsah s slovy "Rychlá", "hnědá" a "Fox".The following code creates a subrange with the words "quick", "brown", and "fox". Zahrnuje words[1] words[3] .It includes words[1] through words[3]. Element words[4] není v rozsahu.The element words[4] isn't in the range.

var quickBrownFox = words[1..4];

Následující kód vytvoří dílčí rozsah s "opožděným" a "pes".The following code creates a subrange with "lazy" and "dog". Zahrnuje words[^2] a words[^1] .It includes words[^2] and words[^1]. Koncový index words[^0] není zahrnutý:The end index words[^0] isn't included:

var lazyDog = words[^2..^0];

V následujících příkladech jsou vytvořeny rozsahy, které jsou otevřeny pro začátek, konec nebo obojí:The following examples create ranges that are open ended for the start, end, or both:

var allWords = words[..]; // contains "The" through "dog".
var firstPhrase = words[..4]; // contains "The" through "fox"
var lastPhrase = words[6..]; // contains "the", "lazy" and "dog"

Rozsahy můžete deklarovat také jako proměnné:You can also declare ranges as variables:

Range phrase = 1..4;

Rozsah lze použít v rámci [ ] znaků a:The range can then be used inside the [ and ] characters:

var text = words[phrase];

Pouze pole podporují indexy a rozsahy.Not only arrays support indices and ranges. Můžete také použít indexy a rozsahy s řetězcem, Span<T> nebo ReadOnlySpan<T> .You can also use indices and ranges with string, Span<T>, or ReadOnlySpan<T>. Další informace najdete v tématu Podpora typů pro indexy a rozsahy.For more information, see Type support for indices and ranges.

Můžete prozkoumat další informace o indexech a oblastech v kurzu týkající se indexů a rozsahů.You can explore more about indices and ranges in the tutorial on indices and ranges.

Přiřazení slučování s hodnotou nullNull-coalescing assignment

Jazyk C# 8,0 zavádí operátor přiřazení s hodnotou null ??= .C# 8.0 introduces the null-coalescing assignment operator ??=. Operátor můžete použít ??= k přiřazení hodnoty jeho pravého operandu jeho levému operandu pouze v případě, že je operand na levé straně vyhodnocen null .You can use the ??= operator to assign the value of its right-hand operand to its left-hand operand only if the left-hand operand evaluates to null.

List<int> numbers = null;
int? i = null;

numbers ??= new List<int>();
numbers.Add(i ??= 17);
numbers.Add(i ??= 20);

Console.WriteLine(string.Join(" ", numbers));  // output: 17 17
Console.WriteLine(i);  // output: 17

Další informace najdete v tématu ?? a?? = – článek o operátorechFor more information, see the ?? and ??= operators article.

Nespravované konstruované typyUnmanaged constructed types

V jazyce C# 7,3 a starším je konstruovaný typ (typ, který obsahuje alespoň jeden argument typu) nespravovaný typ.In C# 7.3 and earlier, a constructed type (a type that includes at least one type argument) can't be an unmanaged type. Počínaje jazykem C# 8,0 je typ konstruované hodnoty nespravovaný, pokud obsahuje pouze pole nespravovaných typů.Starting with C# 8.0, a constructed value type is unmanaged if it contains fields of unmanaged types only.

Například s ohledem na následující definice obecného Coords<T> typu:For example, given the following definition of the generic Coords<T> type:

public struct Coords<T>
{
    public T X;
    public T Y;
}

Coords<int>typ je nespravovaný typ v jazyce C# 8,0 a novějším.the Coords<int> type is an unmanaged type in C# 8.0 and later. Podobně jako u jakéhokoli nespravovaného typu můžete vytvořit ukazatel na proměnnou tohoto typu nebo přidělit blok paměti v zásobníku pro instance tohoto typu:Like for any unmanaged type, you can create a pointer to a variable of this type or allocate a block of memory on the stack for instances of this type:

Span<Coords<int>> coordinates = stackalloc[]
{
    new Coords<int> { X = 0, Y = 0 },
    new Coords<int> { X = 0, Y = 3 },
    new Coords<int> { X = 4, Y = 0 }
};

Další informace naleznete v tématu nespravované typy.For more information, see Unmanaged types.

Stackalloc ve vnořených výrazechStackalloc in nested expressions

Počínaje jazykem C# 8,0, pokud výsledek stackalloc výrazu je System.Span<T> System.ReadOnlySpan<T> typu nebo, můžete použít stackalloc výraz v jiných výrazech:Starting with C# 8.0, if the result of a stackalloc expression is of the System.Span<T> or System.ReadOnlySpan<T> type, you can use the stackalloc expression in other expressions:

Span<int> numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 };
var ind = numbers.IndexOfAny(stackalloc[] { 2, 4, 6, 8 });
Console.WriteLine(ind);  // output: 1

Vylepšení interpolované doslovného řetězceEnhancement of interpolated verbatim strings

Pořadí $ a @ tokeny v interpolované doslovném řetězci mohou být libovolné: $@"..." a @$"..." jsou platné interpolované řetězce.Order of the $ and @ tokens in interpolated verbatim strings can be any: both $@"..." and @$"..." are valid interpolated verbatim strings. V dřívějších verzích C# se $ token musí vyskytovat před @ tokenem.In earlier C# versions, the $ token must appear before the @ token.