Co nowego w języku C# 7.0What's new in C# 7.0

C# 7.0 dodaje wiele nowych funkcji do języka C#:C# 7.0 adds a number of new features to the C# language:

  • out Zmienneout variables
    • Można zadeklarować out wartości wbudowanych jako argumenty do metody, gdzie są używane.You can declare out values inline as arguments to the method where they are used.
  • KrotkiTuples
    • Można tworzyć lekkie, bez nazwy typów, które zawierają wiele pola publiczne.You can create lightweight, unnamed types that contain multiple public fields. Kompilatory i narzędzia IDE zrozumieć semantykę tych typów.Compilers and IDE tools understand the semantics of these types.
  • OdrzuceniaDiscards
    • Odrzuca są tymczasowego, tylko do zapisu zmienne używane w przypisania, jeśli nie dba o wartość przypisana.Discards are temporary, write-only variables used in assignments when you don't care about the value assigned. Są one szczególnie przydatne podczas dekonstrukcja krotek i typy zdefiniowane przez użytkownika, a także podczas wywoływania metody z out parametrów.They are particularly useful when deconstructing tuples and user-defined types, as well as when calling methods with out parameters.
  • Dopasowanie do wzorcaPattern Matching
    • Można utworzyć logikę rozgałęziania, na podstawie dowolnego typu i wartości elementów członkowskich tych typów.You can create branching logic based on arbitrary types and values of the members of those types.
  • ref Zmienne lokalne iref locals and returns
    • Metoda zmienne lokalne i wartości zwracane mogą być odwołania do innych magazynów.Method local variables and return values can be references to other storage.
  • Funkcje lokalneLocal Functions
    • Można zagnieżdżać funkcji w innych funkcjach, aby ograniczyć ich zakres i widoczność.You can nest functions inside other functions to limit their scope and visibility.
  • Więcej elementy członkowskie z wyrażeniemMore expression-bodied members
    • Zwiększył się listę elementów członkowskich, które można tworzyć za pomocą wyrażeń.The list of members that can be authored using expressions has grown.
  • throw Wyrażeniathrow Expressions
    • W konstrukcji kodu, które wcześniej były niedozwolona, ponieważ może generować wyjątki throw został instrukcję.You can throw exceptions in code constructs that previously were not allowed because throw was a statement.
  • Uogólnionego asynchroniczne typy zwracaneGeneralized async return types
    • Metody zadeklarowane za pomocą async modyfikator może zwrócić inne typy oprócz Task i Task<T>.Methods declared with the async modifier can return other types in addition to Task and Task<T>.
  • Ulepszenia składni literału liczbowegoNumeric literal syntax improvements
    • Nowe tokeny poprawić czytelność dla stałych numerycznych.New tokens improve readability for numeric constants.

W pozostałej części tego tematu omawia każdej funkcji.The remainder of this topic discusses each of the features. Dowiesz się jej uzasadnienie, dla każdej funkcji.For each feature, you'll learn the reasoning behind it. Dowiesz się, aby składnia.You'll learn the syntax. Zobaczysz niektóre przykładowe scenariusze, w którym przy użyciu nowej funkcji pozwalają zwiększyć produktywność dla deweloperów.You'll see some sample scenarios where using the new feature will make you more productive as a developer.

out Zmienneout variables

Istniejące składnia, która obsługuje out parametry została ulepszona w tej wersji.The existing syntax that supports out parameters has been improved in this version.

Wcześniej należy oddzielić deklaracja zmiennej poza i inicjacji w dwóch różnych instrukcji:Previously, you would need to separate the declaration of the out variable and its initialization into two different statements:

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

Możesz teraz zadeklarować out zmiennych w liście argumentów wywołania metody, zamiast pisania instrukcji deklaracji oddzielnych: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");

Możesz chcieć określić typ out zmiennych w celu uściślenia, jak pokazano powyżej.You may want to specify the type of the out variable for clarity, as shown above. Język obsługuje jednak przy użyciu niejawnie typizowanej zmiennej lokalnej: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");
  • Kod jest łatwiejsza do odczytania.The code is easier to read.
    • Można zadeklarować zmiennej poza, której używasz, nie w kolejnym wierszu powyżej.You declare the out variable where you use it, not on another line above.
  • Nie ma potrzeby, aby przypisać wartość początkową.No need to assign an initial value.
    • DEKLARUJĄC out zmiennej, w przypadku, gdy jest używany w wywołaniu metody, nie można przypadkowo używać go przed przypisaniem go.By declaring the out variable where it is used in a method call, you can't accidentally use it before it is assigned.

Będzie najczęściej używane dla tej funkcji Try wzorca.The most common use for this feature will be the Try pattern. W tym wzorcu, metoda zwraca bool oznaczający powodzenie lub Niepowodzenie i out zmienna, która zapewnia wynik, jeśli metoda zakończy się powodzeniem.In this pattern, a method returns a bool indicating success or failure and an out variable that provides the result if the method succeeds.

Korzystając z out deklaracja zmiennej zadeklarowanej zmiennej "przecieków", do zakresu zewnętrznego, jeśli instrukcja.When using the out variable declaration, the declared variable "leaks" into the outer scope of the if statement. Dzięki temu można później użyć zmiennej:This allows you to use the variable afterwards:

if (!int.TryParse(input, out int result))
{    
    return null;
}

return result;

KrotkiTuples

Uwaga

Nowe funkcje krotek wymagają ValueTuple typów.The new tuples features require the ValueTuple types. Należy dodać pakiet NuGet System.ValueTuple aby można było używać go na platformach, które nie zawierają typy.You must add the NuGet package System.ValueTuple in order to use it on platforms that do not include the types.

Jest to podobne do innych funkcji języka, które zależą od typów dostarczonych w ramach.This is similar to other language features that rely on types delivered in the framework. Przykład obejmują async i await opierając się na INotifyCompletion interfejsu i LINQ, opierając się na IEnumerable<T>.Example include async and await relying on the INotifyCompletion interface, and LINQ relying on IEnumerable<T>. Jednak mechanizm dostarczania jest zmieniają się wraz z .NET staje się coraz więcej platform niezależne.However, the delivery mechanism is changing as .NET is becoming more platform independent. .NET Framework mogą zawsze są dostarczane na ten sam cykl, jak kompilator języka.The .NET Framework may not always ship on the same cadence as the language compiler. Jeśli nowe funkcje języka, zależą od nowych typów, te typy są dostępne jako pakiety NuGet podczas dostarczania funkcji języka.When new language features rely on new types, those types will be available as NuGet packages when the language features ship. Ponieważ te nowe typy są dodawane do standardowego interfejsu API platformy .NET i dostarczane jako część ram, wymaganie pakietu NuGet zostaną usunięte.As these new types get added to the .NET Standard API and delivered as part of the framework, the NuGet package requirement will be removed.

C# zapewnia rozbudowane składni klas i struktur, który służy do wyjaśnienia intencji Twojego projektu.C# provides a rich syntax for classes and structs that is used to explain your design intent. Jednak czasami takiej składni sformatowanego wymaga dodatkowej pracy dzięki korzyściom z minimalnym.But sometimes that rich syntax requires extra work with minimal benefit. Często mogą zapisywać metod wymagających prostą strukturę zawierających więcej niż jednego elementu danych.You may often write methods that need a simple structure containing more than one data element. Do obsługi tych scenariuszy krotek zostały dodane do języka C#.To support these scenarios tuples were added to C#. Kolekcje są strukturami danych uproszczone, zawierające wiele pól do reprezentowania składowych danych.Tuples are lightweight data structures that contain multiple fields to represent the data members. Pola nie są weryfikowane i nie można definiować własnych metod.The fields are not validated, and you cannot define your own methods

Uwaga

Spójne kolekcje były dostępne przed języka C# 7.0, ale zostały mało wydajne i miał Brak obsługi języka.Tuples were available before C# 7.0, but they were inefficient and had no language support. Oznacza to, że elementy krotki mogą być przywoływane tylko jako Item1, Item2 i tak dalej.This meant that tuple elements could only be referenced as Item1, Item2 and so on. C# 7.0 wprowadza obsługę krotek, co umożliwia semantycznego nazwy pól krotki przy użyciu typy krotki nowe, bardziej wydajne.C# 7.0 introduces language support for tuples, which enables semantic names for the fields of a tuple using new, more efficient tuple types.

Możesz utworzyć krotki przez przypisywanie wartości do każdego elementu członkowskiego:You can create a tuple by assigning a value to each member:

var letters = ("a", "b");

Przypisanie spowoduje utworzenie spójnej kolekcji, której członkami są Item1 i Item2, którego można używać w taki sam sposób jak Tuple można zmienić składnię tworzenia spójną kolekcją, która zawiera semantyczną nazwy do każdego z członków spójna kolekcja znajdująca się:That assignment creates a tuple whose members are Item1 and Item2, which you can use in the same way as Tuple You can change the syntax to create a tuple that provides semantic names to each of the members of the tuple:

(string Alpha, string Beta) namedLetters = ("a", "b");

namedLetters Krotka zawiera pola określone jako Alpha i Beta.The namedLetters tuple contains fields referred to as Alpha and Beta. Te nazwy istnieje tylko w czasie kompilacji i nie są zachowywane w przykładzie, podczas sprawdzania spójna kolekcja znajdująca się w czasie wykonywania przy użyciu odbicia.Those names exist only at compile time and are not preserved for example when inspecting the tuple using reflection at runtime.

W przypisaniu spójnej kolekcji można również określić nazwy pól po prawej stronie przypisania: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");

Po lewej i prawej stronie przypisania, można określić nazwy pól:You can specify names for the fields on both the left and right-hand side of the assignment:

(string First, string Second) firstLetters = (Alpha: "a", Beta: "b");

Wiersz powyżej generuje ostrzeżenie, CS8123, informacją, że nazwy po prawej stronie przypisania, Alpha i Beta są ignorowane, ponieważ są w konflikcie z nazwami po lewej stronie First i Second.The line above generates a warning, CS8123, telling you that the names on the right side of the assignment, Alpha and Beta are ignored because they conflict with the names on the left side, First and Second.

W powyższych przykładach podstawowa składnia do deklarowania krotek.The examples above show the basic syntax to declare tuples. Kolekcje są najbardziej przydatne jako typów zwracanych dla private i internal metody.Tuples are most useful as return types for private and internal methods. Kolekcje zapewniają prostą składnię dla tych metod zwrócić wiele wartości dyskretnych: Zapisywanie pracy tworzenia class lub struct definiujący typ zwracany.Tuples provide a simple syntax for those methods to return multiple discrete values: You save the work of authoring a class or a struct that defines the type returned. Nie ma potrzeby tworzenia nowego typu.There is no need for creating a new type.

Utworzenie spójnej kolekcji jest wydajniejsze i bardziej produktywne.Creating a tuple is more efficient and more productive. Jest prostsze, lekki Składnia umożliwiająca zdefiniowanie struktury danych, który zawiera więcej niż jedną wartość.It is a simpler, lightweight syntax to define a data structure that carries more than one value. Przykładowa metoda poniżej zwraca minimalne i maksymalne wartości znajdujących się w sekwencji liczb całkowitych:The example method below returns the minimum and maximum values found in a sequence of integers:

private static (int Max, int Min) Range(IEnumerable<int> numbers)
{
    int min = int.MaxValue;
    int max = int.MinValue;
    foreach(var n in numbers)
    {
        min = (n < min) ? n : min;
        max = (n > max) ? n : max;
    }
    return (max, min);
}

Możliwość korzystania z krotek w ten sposób oferuje kilka dodatkowych korzyści:Using tuples in this way offers several advantages:

  • Zapisywanie pracy tworzenia class lub struct definiujący typ zwracany.You save the work of authoring a class or a struct that defines the type returned.
  • Nie trzeba tworzyć nowego typu.You do not need to create new type.
  • Ulepszenia języka eliminuje konieczność kontaktowania się z Create<T1>(T1) metody.The language enhancements removes the need to call the Create<T1>(T1) methods.

W deklaracji metody udostępnia nazwy pól spójną kolekcją, która jest zwracana.The declaration for the method provides the names for the fields of the tuple that is returned. Po wywołaniu metody, wartość zwracana jest spójna kolekcja, w których pola są Max i Min:When you call the method, the return value is a tuple whose fields are Max and Min:

var range = Range(numbers);

Mogą wystąpić sytuacje, gdy zachodzi potrzeba rozpakować elementy członkowskie spójnej kolekcji, które zostały zwrócone z metody.There may be times when you want to unpackage the members of a tuple that were returned from a method. Możecie od zadeklarowania zmiennych osobne dla każdej wartości w spójnej kolekcji.You can do that by declaring separate variables for each of the values in the tuple. Jest to nazywane dekonstrukcja spójnej kolekcji:This is called deconstructing the tuple:

(int max, int min) = Range(numbers);

Możesz też podać podobne dekonstrukcja dla dowolnego typu na platformie .NET.You can also provide a similar deconstruction for any type in .NET. Jest to realizowane przez zapisywanie Deconstruct metodę jako składową klasy.This is done by writing a Deconstruct method as a member of the class. Czy Deconstruct metoda zawiera zbiór out argumenty dla każdej właściwości, które mają zostać wyodrębnione.That Deconstruct method provides a set of out arguments for each of the properties you want to extract. Należy wziąć pod uwagę to Point klasę, która zapewnia metody deconstructor, która wyodrębnia X i Y współrzędnych: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)
    {
        this.X = x;
        this.Y = y;
    }

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

    public void Deconstruct(out double x, out double y)
    {
        x = this.X;
        y = this.Y;
    }
}

Można wyodrębnić poszczególne pola, przypisując Point do krotki: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;

Nie są powiązane przez nazwy zdefiniowane w Deconstruct metody.You are not bound by the names defined in the Deconstruct method. W ramach przypisania, można zmienić nazwy zmiennych wyodrębniania:You can rename the extract variables as part of the assignment:

(double horizontalDistance, double verticalDistance) = p;

Możesz dowiedzieć się w bardziej szczegółowe informacje o krotek w tematu krotek.You can learn more in depth about tuples in the tuples topic.

OdrzucaDiscards

Często podczas dekonstrukcja krotki lub wywołanie metody z out parametrami, jest zmuszony do definiowania zmiennej z żądanymi wartościami nie interesuje i nie będą używane.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# dodaje obsługę odrzuca do obsługi tego scenariusza.C# adds support for discards to handle this scenario. Odrzucenia to zmienna tylko do zapisu, którego nazwa jest _ (znaki podkreślenia); można przypisać wszystkie wartości, które zamierzasz odrzucić do jednej zmiennej.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. Odrzucenia przypomina nieprzypisanej zmiennej; Oprócz instrukcji przypisania odrzuceń, nie można użyć w kodzie.A discard is like an unassigned variable; apart from the assignment statement, the discard can't be used in code.

Odrzuca są obsługiwane w następujących scenariuszach:Discards are supported in the following scenarios:

  • Gdy dekonstrukcja krotek lub typy zdefiniowane przez użytkownika.When deconstructing tuples or user-defined types.

  • Podczas wywoływania metody z się parametrów.When calling methods with out parameters.

  • We wzorcu, dopasowanie operację, podając jest i Przełącz instrukcji.In a pattern matching operation with the is and switch statements.

  • Jako identyfikator autonomicznej, aby jawnie Zidentyfikuj wartość do przypisania jako odrzucenia.As a standalone identifier when you want to explicitly identify the value of an assignment as a discard.

W poniższym przykładzie zdefiniowano QueryCityDataForYears metodę, która zwraca 6-spójną kolekcją, która zawiera dane jako uczestnik w mieście dla dwóch różnych lat.The following example defines a QueryCityDataForYears method that returns a 6-tuple that contains a data for a city for two different years. Wywołanie metody w przykładzie dotyczy tylko wartości dwóch populacji zwracany przez metodę i dlatego traktuje pozostałe wartości w spójnej kolekcji, ponieważ odrzuca, gdy jej deconstructs spójnej kolekcji.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

Aby uzyskać więcej informacji, zobacz odrzuca.For more information, see Discards.

Dopasowanie wzorcaPattern matching

Dopasowanie wzorca jest funkcją, która pozwala na implementowanie metody wysyłania we właściwościach innych niż typ obiektu.Pattern matching is a feature that allows you to implement method dispatch on properties other than the type of an object. Prawdopodobnie znasz metodę alokacji na podstawie typu obiektu.You're probably already familiar with method dispatch based on the type of an object. W obiektowo programowania, wirtualnych i zastąpienie metody zapewniają składnię języka, aby zaimplementować metodę wysyłania na podstawie typu obiektu.In Object Oriented programming, virtual and override methods provide language syntax to implement method dispatching based on an object's type. Podstawowe i pochodne klasy dostarczać różne implementacje.Base and Derived classes provide different implementations. Wyrażenia dopasowania wzorca rozszerzyć tę koncepcję, dzięki czemu można łatwo zaimplementować podobnych wzorców wysyłania dla typów i danych elementów, które nie są powiązane przez hierarchię dziedziczenia.Pattern matching expressions extend this concept so that you can easily implement similar dispatch patterns for types and data elements that are not related through an inheritance hierarchy.

Obsługa dopasowania do wzorca is wyrażeń i switch wyrażenia.Pattern matching supports is expressions and switch expressions. Każdy umożliwia zapoznanie się obiekt i jego właściwości w celu stwierdzenia, jeśli ten obiekt spełnia wzorzec używanych.Each enables inspecting an object and its properties to determine if that object satisfies the sought pattern. Możesz użyć when — słowo kluczowe, aby określić dodatkowe reguły do wzorca.You use the when keyword to specify additional rules to the pattern.

is Wyrażenieis expression

is Wyrażenia wzorca rozszerza znanej is operator, aby wysłać zapytania do obiektu poza jego typu.The is pattern expression extends the familiar is operator to query an object beyond its type.

Zacznijmy od prostego scenariusza.Let's start with a simple scenario. Dodamy możliwości tego scenariusza, które pokazują, jak wzorzec dopasowywania wyrażeniach algorytmów, współpracujących z niepowiązanych typy proste.We'll add capabilities to this scenario that demonstrate how pattern matching expressions make algorithms that work with unrelated types easy. Rozpoczniemy od metody, która oblicza sumę liczby ustala struktury:We'll start with a method that computes the sum of a number of die rolls:

public static int DiceSum(IEnumerable<int> values)
{
    return values.Sum();
}

Może szybko się okazać konieczne obliczający sumę ustala struktury, w której niektóre ustala są przekazywane przy użyciu wielu dice (dice jest struktury w liczbie mnogiej).You might quickly find that you need to find the sum of die rolls where some of the rolls are made with multiple dice (dice is the plural of die). Część sekwencji wejściowych może być wiele wyników, zamiast jednego numeru:Part of the input sequence may be multiple results instead of a single number:

public static int DiceSum2(IEnumerable<object> values)
{
    var sum = 0;
    foreach(var item in values)
    {
        if (item is int val)
            sum += val;
        else if (item is IEnumerable<object> subList)
            sum += DiceSum2(subList);
    }
    return sum;
}

is Wyrażenia wzorca działa bardzo dobrze w tym scenariuszu.The is pattern expression works quite well in this scenario. W ramach Sprawdzanie typu można napisać inicjalizacji zmiennej.As part of checking the type, you write a variable initialization. Spowoduje to utworzenie nowej zmiennej typu zweryfikowanych środowiska uruchomieniowego.This creates a new variable of the validated runtime type.

Jak zachować rozszerzenie tych scenariuszy, może się okazać Konstruuj więcej if i else if instrukcji.As you keep extending these scenarios, you may find that you build more if and else if statements. Gdy stanie się nieporęczny, prawdopodobnie należy przełączyć się do switch wzorca wyrażenia.Once that becomes unwieldy, you'll likely want to switch to switch pattern expressions.

switch aktualizacje — instrukcjaswitch statement updates

Pasuje do wyrażenia ma dobrze znanej składni, w oparciu o switch instrukcja jest już częścią języka C#.The match expression has a familiar syntax, based on the switch statement already part of the C# language. Umożliwia tłumaczenie istniejącego kodu do wyrażenia dopasowania przed dodaniem nowych przypadków:Let's translate the existing code to use a match expression before adding new cases:

public static int DiceSum3(IEnumerable<object> values)
{
    var sum = 0;
    foreach (var item in values)
    {
        switch (item)
        {
            case int val:
                sum += val;
                break;
            case IEnumerable<object> subList:
                sum += DiceSum3(subList);
                break;
        }
    }
    return sum;
}

Wyrażenia dopasowania mają nieco składnią niż is wyrażenia, gdzie możesz deklarować typ i zmiennej na początku case wyrażenia.The match expressions have a slightly different syntax than the is expressions, where you declare the type and variable at the beginning of the case expression.

Wyrażenia dopasowania również obsługiwać stałe.The match expressions also support constants. Aby zaoszczędzić czas, to wyprowadzenie się proste przypadki:This can save time by factoring out simple cases:

public static int DiceSum4(IEnumerable<object> values)
{
    var sum = 0;
    foreach (var item in values)
    {
        switch (item)
        {
            case 0:
                break;
            case int val:
                sum += val;
                break;
            case IEnumerable<object> subList when subList.Any():
                sum += DiceSum4(subList);
                break;
            case IEnumerable<object> subList:
                break;
            case null:
                break;
            default:
                throw new InvalidOperationException("unknown item type");
        }
    }
    return sum;
}

Powyższy kod dodaje przypadków 0 w szczególnych przypadkach z int, i null w szczególnych przypadkach, gdy nie wprowadza żadnych danych.The code above adds cases for 0 as a special case of int, and null as a special case when there is no input. W tym przykładzie pokazano jeden nową ważną funkcję w wyrażeniach wzorzec przełącznika: kolejność case sprawach teraz wyrażeń.This demonstrates one important new feature in switch pattern expressions: the order of the case expressions now matters. 0 Case musi występować przed ogólnym int przypadek.The 0 case must appear before the general int case. W przeciwnym razie byłoby pierwszy wzorzec do dopasowania int przypadku, nawet wtedy, gdy wartość jest 0.Otherwise, the first pattern to match would be the int case, even when the value is 0. Jeśli taki sposób, że w przypadku nowszych została już obsłużona przypadkowo kolejność wyrażenia dopasowań, kompilator będzie Flaga, która i wygenerowanie błędu.If you accidentally order match expressions such that a later case has already been handled, the compiler will flag that and generate an error.

To takie samo zachowanie umożliwia specjalne przypadku pustą sekwencją danych wejściowych.This same behavior enables the special case for an empty input sequence. Możesz zobaczyć, że w przypadku IEnumerable element, który ma elementów musi występować przed ogólnym IEnumerable przypadek.You can see that the case for an IEnumerable item that has elements must appear before the general IEnumerable case.

Tej wersji dodano również default przypadek.This version has also added a default case. default Przypadek jest obliczane zawsze ostatnio, niezależnie od kolejności pojawiają się one w źródle.The default case is always evaluated last, regardless of the order it appears in the source. Z tego powodu Konwencji polega na umieszczeniu default ostatniego wielkości liter.For that reason, convention is to put the default case last.

Na koniec możemy dodać jeden ostatni case dla nowego stylu struktury.Finally, let's add one last case for a new style of die. Niektóre gry umożliwia dice percentyl reprezentują większych zakresy liczb.Some games use percentile dice to represent larger ranges of numbers.

Uwaga

Dwa dice dwustronna 10 percentylu mogą reprezentować każdy numer z zakresu od 0 do 99.Two 10-sided percentile dice can represent every number from 0 through 99. Struktury co ma boki oznakowane 00, 10, 20,... 90.One die has sides labelled 00, 10, 20, ... 90. Inne struktury ma boki etykietą 0, 1, 2,... 9.The other die has sides labeled 0, 1, 2, ... 9. Dodaj wartości dwie struktury ze sobą, a każdy numer można uzyskać z zakresu od 0 do 99.Add the two die values together and you can get every number from 0 through 99.

Aby dodać tego rodzaju struktury do kolekcji, należy najpierw zdefiniować typu reprezentującego dice percentyl.To add this kind of die to your collection, first define a type to represent the percentile dice. TensDigit Właściwość przechowuje wartości 0, 10, 20, maksymalnie 90:The TensDigit property stores values 0, 10, 20, up to 90:

public struct PercentileDice
{
    public int OnesDigit { get; }
    public int TensDigit { get; }

    public PercentileDice(int tensDigit, int onesDigit)
    {
        this.OnesDigit = onesDigit;
        this.TensDigit = tensDigit;
    }
}

Następnie należy dodać case pasuje do wyrażenia dla nowego typu:Then, add a case match expression for the new type:

public static int DiceSum5(IEnumerable<object> values)
{
    var sum = 0;
    foreach (var item in values)
    {
        switch (item)
        {
            case 0:
                break;
            case int val:
                sum += val;
                break;
            case PercentileDice dice:
                sum += dice.TensDigit + dice.OnesDigit;
                break;
            case IEnumerable<object> subList when subList.Any():
                sum += DiceSum5(subList);
                break;
            case IEnumerable<object> subList:
                break;
            case null:
                break;
            default:
                throw new InvalidOperationException("unknown item type");
        }
    }
    return sum;
}

Składnia nowych wyrażeniach dopasowania do wzorca ułatwia tworzenie algorytmów alokacji na podstawie typu obiektu lub innych właściwości, za pomocą składni bardziej czytelne i zwięzłe.The new syntax for pattern matching expressions makes it easier to create dispatch algorithms based on an object's type, or other properties, using a clear and concise syntax. Wyrażenia dopasowania wzorca Włącz te konstrukcje na typy danych, które są niezwiązanych ze sobą przez dziedziczenie.Pattern matching expressions enable these constructs on data types that are unrelated by inheritance.

Dowiedz się więcej o dopasowywaniu do wzorca w temacie dedykowane dopasowywania wzorca w języku C#.You can learn more about pattern matching in the topic dedicated to pattern matching in C#.

Zmienne lokalne ref i zwracaRef locals and returns

Ta funkcja umożliwia algorytmy, które używają i zwracać odwołań do zmiennych określonych gdzie indziej.This feature enables algorithms that use and return references to variables defined elsewhere. Przykładem jest praca z macierzy dużych i znajdowanie w jednej lokalizacji, z określoną wspólną charakterystykę.One example is working with large matrices, and finding a single location with certain characteristics. Jedna metoda zwróci dwa indeksy dla jednej lokalizacji w macierzy:One method would return the two indices for a single location in the matrix:

public static (int i, int j) 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 (i, j);
    return (-1, -1); // Not found
}

Istnieje wiele problemów przy użyciu tego kodu.There are many issues with this code. Przede wszystkim jest publiczną metodę, która zwraca spójną kolekcję.First of all, it's a public method that's returning a tuple. Język obsługuje to jednak typów zdefiniowanych przez użytkownika (klasy lub struktury) są preferowane dla publicznych interfejsów API.The language supports this, but user defined types (either classes or structs) are preferred for public APIs.

Po drugie ta metoda zwraca wskaźników do elementów w macierzy.Second, this method is returning the indices to the item in the matrix. Prowadzi to obiekty wywołujące do pisania kodu, który używa tych indeksów w celu wyłuskania macierzy i modyfikowania pojedynczego elementu:That leads callers to write code that uses those indices to dereference the matrix and modify a single element:

var indices = MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(indices);
matrix[indices.i, indices.j] = 24;

Należy zamiast napisać metodę, która zwraca odwołania do elementu macierzy, który chcesz zmienić.You'd rather write a method that returns a reference to the element of the matrix that you want to change. Tylko można to osiągnąć, używając niebezpieczny kod i zwraca wskaźnik do int w poprzednich wersjach.You could only accomplish this by using unsafe code and returning a pointer to an int in previous versions.

Przejdźmy teraz przez szereg zmian, które przedstawiono tu funkcji lokalnego odwołania i pokazują, jak utworzyć metodę, która zwraca odwołanie do pamięci wewnętrznej.Let's walk through a series of changes to demonstrate the ref local feature and show how to create a method that returns a reference to internal storage. Po drodze dowiesz się regułami zwracane ref i funkcji lokalnych ref, która chroni przypadkowym niewłaściwie korzysta.Along the way, you'll learn the rules of the ref return and ref local feature that protects you from accidentally misusing it.

Rozpoczynanie pracy od modyfikowania Find deklaracji metody, tak że zwraca ref int zamiast spójnej kolekcji.Start by modifying the Find method declaration so that it returns a ref int instead of a tuple. Następnie zmodyfikuj instrukcję return, tak aby zwracało poprawnie wartość przechowywaną w macierzy, zamiast dwóch wskaźników:Then, modify the return statement so it returns the value stored in the matrix instead of the two indices:

// Note that this won't compile. 
// Method declaration indicates ref return,
// but return statement specifies a value return.
public static ref int Find2(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 matrix[i, j];
    throw new InvalidOperationException("Not found");
}

Kiedy Deklarujesz, metoda zwraca ref zmiennych, musisz również dodać ref — słowo kluczowe do każdej instrukcji return.When you declare that a method returns a ref variable, you must also add the ref keyword to each return statement. Oznacza to zwracanie przez odwołanie, a następnie pomaga deweloperom czytania dalszej części kodu, należy pamiętać, że metoda zwraca wartość przez odwołanie:That indicates return by reference, and helps developers reading the code later remember that the method returns by reference:

public static ref int Find3(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");
}

Teraz, metoda zwraca odwołanie do wartości całkowitej w macierzy, należy zmodyfikować, gdzie jest wywoływana.Now that the method returns a reference to the integer value in the matrix, you need to modify where it's called. var Deklaracji oznacza, że valItem jest teraz int zamiast spójnej kolekcji:The var declaration means that valItem is now an int rather than a tuple:

var valItem = MatrixSearch.Find3(matrix, (val) => val == 42);
Console.WriteLine(valItem);
valItem = 24;
Console.WriteLine(matrix[4, 2]);

Drugi WriteLine instrukcja w powyższym przykładzie drukuje się wartość 42, a nie 24.The second WriteLine statement in the example above prints out the value 42, not 24. Zmienna valItem jest int, a nie ref int.The variable valItem is an int, not a ref int. var — Słowo kluczowe umożliwia kompilatorowi określić typ, ale nie spowoduje dodanie niejawnie ref modyfikator.The var keyword enables the compiler to specify the type, but will not implicitly add the ref modifier. Zamiast tego wartość odwołuje się ref return jest skopiowane do zmiennej po lewej stronie przypisania.Instead, the value referred to by the ref return is copied to the variable on the left-hand side of the assignment. Zmienna nie jest ref lokalnego.The variable is not a ref local.

Aby uzyskać wynik ma, musisz dodać ref modyfikatora deklaracji zmiennej lokalnej do ustaw dla zmiennej odwołania, gdy wartość zwracana jest odwołanie:In order to get the result you want, you need to add the ref modifier to the local variable declaration to make the variable a reference when the return value is a reference:

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

Teraz, drugi WriteLine instrukcja w powyższym przykładzie zostanie wydrukowana wartość 24, wskazującą, czy zostały zmodyfikowane magazynu w macierzy.Now, the second WriteLine statement in the example above will print out the value 24, indicating that the storage in the matrix has been modified. Zmienna lokalna została zadeklarowana przy użyciu ref modyfikator i potrwa ref powrócić.The local variable has been declared with the ref modifier, and it will take a ref return. Należy zainicjować ref zmiennej gdy jest on zadeklarowany, nie można podzielić deklaracji i inicjowania.You must initialize a ref variable when it is declared, you cannot split the declaration and the initialization.

W języku C# ma trzy inne reguły, które można chronić przed używaniem ref zmiennych lokalnych i zwraca:The C# language has three other rules that protect you from misusing the ref locals and returns:

  • Nie można przypisać wartość zwracaną standardową metodę, aby ref zmiennej lokalnej.You cannot assign a standard method return value to a ref local variable.
    • Które nie zezwalają na instrukcjach, takich jak ref int i = sequence.Count();That disallows statements like ref int i = sequence.Count();
  • Nie można zwrócić ref do zmiennej, którego okres istnienia nie wykracza poza wykonywanie metody.You cannot return a ref to a variable whose lifetime does not extend beyond the execution of the method.
    • Oznacza to, że nie można zwrócić odwołanie do zmiennej lokalnej lub zmienną o zakresie podobne.That means you cannot return a reference to a local variable or a variable with a similar scope.
  • ref Zmienne lokalne i nie można używać metod asynchronicznych.ref locals and returns can't be used with async methods.
    • Kompilator nie wiadomo, jeśli przywoływany została ustawiona zmienna końcowej wartości po powrocie z metody asynchronicznej.The compiler can't know if the referenced variable has been set to its final value when the async method returns.

Zmienne lokalne ref i ref zwraca algorytmy Włącz, które są bardziej wydajne, unikając kopiowania wartości lub wykonywanie operacji dereferencji wiele razy.The addition of ref locals and ref returns enable algorithms that are more efficient by avoiding copying values, or performing dereferencing operations multiple times.

Dodawanie ref wartość zwracana jest źródła zmiany zgodne.Adding ref to the return value is a source compatible change. Kompiluje istniejący kod, ale odwołania zwracana wartość jest kopiowany po przypisaniu.Existing code compiles, but the ref return value is copied when assigned. Obiekty wywołujące muszą zaktualizować magazyn dla zwracanej wartości ref zmienną lokalną, aby przechowywać zwracany jako odwołanie.Callers must update the storage for the return value to a ref local variable to store the return as a reference.

Aby uzyskać więcej informacji, zobacz ref — słowo kluczowe artykułu.For more information, see the ref keyword article.

Funkcje lokalneLocal functions

Wiele projektów dla klasy zawierają metody, które są wywoływane z tylko jedną lokalizację.Many designs for classes include methods that are called from only one location. Te dodatkowe metody prywatnej Zachowaj każdej metody niewielkiego i skupionego projektu.These additional private methods keep each method small and focused. Jednak ich może utrudnić zrozumienie klasę, odczytując je po raz pierwszy.However, they can make it harder to understand a class when reading it the first time. Te metody musi być rozumiane poza kontekstem jednej lokalizacji wywoływania.These methods must be understood outside of the context of the single calling location.

Te projekty funkcje lokalne umożliwiają deklarowanie metody w kontekście innej metody.For those designs, local functions enable you to declare methods inside the context of another method. To ułatwia czytelnicy klasy, aby zobaczyć tylko wywołać metody lokalnej z kontekstu, w którym jest zadeklarowana.This makes it easier for readers of the class to see that the local method is only called from the context in which is it declared.

Istnieją dwa przypadki użycia bardzo często dla funkcji lokalnych: metody iteratora publicznych i metod asynchronicznych publicznych.There are two very common use cases for local functions: public iterator methods and public async methods. Oba rodzaje metod wygenerować kod, który zgłasza błędy nowsze niż programiści mogą oczekiwać.Both types of methods generate code that reports errors later than programmers might expect. W przypadku metody iteracyjne wszelkie wyjątki pojawiają się tylko podczas wywoływania kodu, który wylicza zwracanej sekwencji.In the case of iterator methods, any exceptions are observed only when calling code that enumerates the returned sequence. W przypadku metod asynchronicznych wyjątków tylko obserwuje się kiedy zwracanego Task jest oczekiwane.In the case of async methods, any exceptions are only observed when the returned Task is awaited.

Zacznijmy od metody iteratora:Let's start with an iterator method:

public static IEnumerable<char> AlphabetSubset(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)}");
    for (var c = start; c < end; c++)
        yield return c;
}

Sprawdź poniższy kod, który wywołuje metodę iteratora niepoprawnie:Examine the code below that calls the iterator method incorrectly:

var resultSet = Iterator.AlphabetSubset('f', 'a');
Console.WriteLine("iterator created");
foreach (var thing in resultSet)
    Console.Write($"{thing}, ");

Wyjątek jest generowany, gdy resultSet jest postanowiliśmy, nie po resultSet zostanie utworzony.The exception is thrown when resultSet is iterated, not when resultSet is created. W tym przykładzie zawartej większość deweloperów może szybko zdiagnozować problem.In this contained example, most developers could quickly diagnose the problem. Jednak w większych bazach kodu, kod, który tworzy iterator, często nie jest zbliżony kod, który wylicza wynik.However, in larger codebases, the code that creates an iterator often isn't as close to the code that enumerates the result. Tak, aby publiczny metoda sprawdza wszystkie argumenty i metody prywatnej generuje wyliczenia mogą refaktoryzować kod:You can refactor the code so that the public method validates all arguments, and a private method generates the enumeration:

public static IEnumerable<char> AlphabetSubset2(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(start, end);
}

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

Ta wersja wycofanej będzie zgłaszają wyjątki od razu, ponieważ metoda publiczna nie jest metodą iteratora; używa metody prywatnej yield return składni.This refactored version will throw exceptions immediately because the public method is not an iterator method; only the private method uses the yield return syntax. Jednak istnieją potencjalne problemy z tej refaktoryzacji.However, there are potential problems with this refactoring. Metoda prywatna tylko powinna być wywoływana z metody interfejsu publicznego, ponieważ w przeciwnym razie całej walidacji argument zostanie pominięty.The private method should only be called from the public interface method, because otherwise all argument validation is skipped. Czytelnicy klasy ten fakt muszą zostać odnalezione, odczytując całej klasy i wyszukiwania odwołań do alphabetSubsetImplementation metody.Readers of the class must discover this fact by reading the entire class and searching for any other references to the alphabetSubsetImplementation method.

Umożliwia tym przeznaczeniem projektowania bardziej zrozumiałe przez zadeklarowanie alphabetSubsetImplementation jako funkcja lokalna wewnątrz publicznej metody interfejsu API:You can make that design intent more clear by declaring the alphabetSubsetImplementation as a local function inside the public API method:

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

Wersja powyżej sprawia, że wyczyść przywoływanymi lokalną metodę tylko w kontekście metodzie zewnętrznej.The version above makes it clear that the local method is referenced only in the context of the outer method. Reguły dotyczące funkcji lokalnych upewnij się, Projektant nie może przypadkowo wywołania funkcji lokalnej z innej lokalizacji w klasie i pominąć walidacji argumentów.The rules for local functions also ensure that a developer can't accidentally call the local function from another location in the class and bypass the argument validation.

Można zastosować za pomocą tej samej techniki async metod w celu zapewnienia, że wynikające z walidacji argumentów zgłaszania wyjątków przed rozpoczęciem pracy asynchronicznej: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.";
    }
}

Uwaga

Niektóre projekty, które są obsługiwane przez funkcje lokalne mogą być również wykonywane przy użyciu wyrażeń lambda.Some of the designs that are supported by local functions could also be accomplished using lambda expressions. Te zainteresowane można Dowiedz się więcej o różnicachThose interested can read more about the differences

Więcej elementy członkowskie z wyrażeniemMore expression-bodied members

C# 6 wprowadzono elementy członkowskie z wyrażeniem dla elementów członkowskich i właściwości tylko do odczytu.C# 6 introduced expression-bodied members for member functions, and read-only properties. C# 7.0 rozwija dozwolonych elementów członkowskich, które można zaimplementować jako wyrażenia.C# 7.0 expands the allowed members that can be implemented as expressions. W języku C# 7.0, można zaimplementować konstruktory, finalizatory, i get i set metod dostępu na właściwości i indeksatorów .In C# 7.0, you can implement constructors, finalizers, and get and set accessors on properties and indexers. Poniższy kod przedstawia przykłady każdego z nich: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";
}

Uwaga

W tym przykładzie nie potrzebuje finalizatora, jednak wyświetleniem pokazano składnię.This example does not need a finalizer, but it is shown to demonstrate the syntax. Nie należy implementować finalizator w klasie, chyba że jest to niezbędne zwolnić niezarządzane zasoby.You should not implement a finalizer in your class unless it is necessary to release unmanaged resources. Należy również rozważyć użycie SafeHandle klasy zamiast na zarządzaniu bezpośrednio niezarządzane zasoby.You should also consider using the SafeHandle class instead of managing unmanaged resources directly.

Te nowe lokalizacje dla elementy członkowskie z wyrażeniem reprezentują ważnym kamieniem milowym dla C# języka: Te funkcje zostały zaimplementowane przez członków społeczności nad typu open-source Roslyn projektu.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.

Zmiana metody do elementu członkowskiego zabudowanego wyrażenie jest binarne zmiany zgodne.Changing a method to an expression bodied member is a binary compatible change.

Wyrażeń throwThrow expressions

W języku C# throw zawsze było instrukcję.In C#, throw has always been a statement. Ponieważ throw znajduje się zdanie, nie wyrażenia, były konstrukcji języka C#, w których nie można go.Because throw is a statement, not an expression, there were C# constructs where you could not use it. Uwzględnione są niektóre wyrażenia lambda, wyrażenia warunkowe i wyrażenia łączące wartości null.These included conditional expressions, null coalescing expressions, and some lambda expressions. Dodanie elementy członkowskie z wyrażeniem dodanie większej liczby lokalizacji gdzie throw wyrażeń może okazać się przydatne.The addition of expression-bodied members adds more locations where throw expressions would be useful. Dzięki czemu można tworzyć dowolne te konstrukcje, C# 7.0 wprowadza wyrażeń throw.So that you can write any of these constructs, C# 7.0 introduces throw expressions.

Składnia jest taka sama jak zawsze użytej w przypadku throw instrukcji.The syntax is the same as you've always used for throw statements. Jedyną różnicą jest to, że teraz można umieścić je w nowej lokalizacji, takich jak w wyrażeniu warunkowym:The only difference is that now you can place them in new locations, such as in a conditional expression:

public string Name
{
    get => name;
    set => name = value ?? 
        throw new ArgumentNullException(paramName: nameof(value), message: "New name must not be null");
}

Umożliwia używanie wyrażenia throw w wyrażeniach inicjacji to funkcje:This features enables using throw expressions in initialization expressions:

private ConfigResource loadedConfig = LoadConfigResourceOrDefault() ?? 
    throw new InvalidOperationException("Could not load config");

Wcześniej te inicjalizacje musi być w konstruktorze, za pomocą instrukcji throw w treści konstruktora:Previously, those initializations would need to be in a constructor, with the throw statements in the body of the constructor:

public ApplicationOptions()
{
    loadedConfig = LoadConfigResourceOrDefault();
    if (loadedConfig == null)
        throw new InvalidOperationException("Could not load config");

}

Uwaga

Zarówno konstrukcji poprzedniego spowoduje, że wyjątki zostanie wygenerowany podczas konstruowania obiektu.Both of the preceding constructs will cause exceptions to be thrown during the construction of an object. Te są często trudne do sprawności.Those are often difficult to recover from. Z tego powodu zaleca się projekty, które zgłaszają wyjątki podczas konstruowania.For that reason, designs that throw exceptions during construction are discouraged.

Uogólnionego asynchroniczne typy zwracaneGeneralized async return types

Zwracanie Task obiekt z metody asynchronicznej może prowadzić do wąskich gardeł wydajności w niektórych ścieżek.Returning a Task object from async methods can introduce performance bottlenecks in certain paths. Task jest typem referencyjnym, więc za jego pomocą oznacza przydzielanie obiektu.Task is a reference type, so using it means allocating an object. W przypadkach, w którym metoda jest zadeklarowana za pomocą async modyfikator zwraca wynik pamięci podręcznej lub zakończeniu synchronicznie, dodatkowe alokacji może stać się koszt znaczną ilość czasu, w sekcji krytycznych wydajność kodu.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. Mogą stać się bardzo kosztowna, jeśli te przydziały występują w ścisłej pętli.It can become very costly if those allocations occur in tight loops.

Nowa funkcja języka oznacza, że metody asynchroniczne mogą zwracać inne typy oprócz Task, Task<T> i void.The new language feature means that async methods may return other types in addition to Task, Task<T> and void. Zwrócony typ nadal musi spełniać wzorca asynchronicznego, co oznacza GetAwaiter metody muszą być dostępne.The returned type must still satisfy the async pattern, meaning a GetAwaiter method must be accessible. Przykład jednego konkretnego ValueTask typ został dodany do programu .NET framework, aby skorzystać z tej nowej funkcji języka:As one concrete example, the ValueTask type has been added to the .NET framework to make use of this new language feature:

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

Uwaga

Dodaj pakiet NuGet System.Threading.Tasks.Extensions aby można było używać ValueTask<TResult> typu.You need to add the NuGet package System.Threading.Tasks.Extensions in order to use the ValueTask<TResult> type.

Proste optymalizacji byłoby użycie ValueTask w miejscach gdzie Task będzie używana przed.A simple optimization would be to use ValueTask in places where Task would be used before. Jednak jeśli chcesz ręcznie przeprowadzić dodatkowe optymalizacje, można buforować wyniki z Praca asynchroniczna i wielokrotnie używać wyników w kolejnych wywołaniach.However, if you want to perform extra optimizations by hand, you can cache results from async work and reuse the result in subsequent calls. ValueTask Struktura ma konstruktora z Task parametru, aby można skonstruować ValueTask z wartość zwracaną metody asynchronicznej, wszelkie istniejące:The ValueTask struct has a constructor with a Task parameter so that you can construct a ValueTask from the return value of any existing async method:

public ValueTask<int> CachedFunc()
{
    return (cache) ? new ValueTask<int>(cacheResult) : new ValueTask<int>(LoadCache());
}
private bool cache = false;
private int cacheResult;
private async Task<int> LoadCache()
{
    // simulate async work:
    await Task.Delay(100);
    cacheResult = 100;
    cache = true;
    return cacheResult;
}

Zgodnie z wszystkich zaleceń dotyczących wydajności należy benchmark obie wersje przed wprowadzeniem dużych skalowania zmiany w kodzie.As with all performance recommendations, you should benchmark both versions before making large scale changes to your code.

Jeśli wartość zwracana jest elementem docelowym await instrukcji, zmiana interfejsu API z poziomu Task<TResult> do ValueTask<TResult> jest źródła zmiany zgodne.When the return value is the target of an await statement, changing an API from a Task<TResult> to a ValueTask<TResult> is a source compatible change. Ogólnie rzecz biorąc, zmiana ValueTask nie jest.In general, changing to ValueTask is not.

Ulepszenia składni literału liczbowegoNumeric literal syntax improvements

Misreading stałych numerycznych może utrudnić rozumienie kodu, odczytując je po raz pierwszy.Misreading numeric constants can make it harder to understand code when reading it for the first time. Często dzieje się tak po tych dwóch liczb służą jako masek bitowych lub innych symbolicznego zamiast wartości liczbowych.This often occurs when those numbers are used as bit masks or other symbolic rather than numeric values. C# 7.0 zawiera dwie nowe funkcje, aby ułatwić zapisywać liczby w najbardziej czytelny sposób związane z zamierzonym użyciem: literały binarne, i separatory cyfr.C# 7.0 includes two new features to make it easier to write numbers in the most readable fashion for the intended use: binary literals, and digit separators.

Czasów podczas tworzenia masek bitowych lub zawsze wtedy, gdy binarna reprezentacja liczby sprawia, że najbardziej czytelne kodu, zapisać tę liczbę w pliku binarnego:For those times when you are creating bit masks, or whenever a binary representation of a number makes the most readable code, write that number in binary:

public const int One =  0b0001;
public const int Two =  0b0010;
public const int Four = 0b0100;
public const int Eight = 0b1000;

0b Na początku stała wskazuje, że liczba jest zapisywany jako wartości binarnej.The 0b at the beginning of the constant indicates that the number is written as a binary number.

Binarne wartości można uzyskać bardzo długie, więc jest często łatwiej zobaczyć wzorców bitowych, wprowadzając _ jako separator cyfr:Binary numbers can get very long, so it's often easier to see the bit patterns by introducing the _ as a digit separator:

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

Separator cyfr może występować w dowolnym miejscu w stałej.The digit separator can appear anywhere in the constant. W przypadku podstawowej liczb 10, będzie często używa się tysięcy separatora:For base 10 numbers, it would be common to use it as a thousands separator:

public const long BillionsAndBillions = 100_000_000_000;

Separator cyfr, może być używany z decimal, float i double również typy: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;

Razem wzięte, można zadeklarować stałych numerycznych za pomocą znacznie więcej czytelności.Taken together, you can declare numeric constants with much more readability.