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

W języku c# 9,0 dodano następujące funkcje i ulepszenia języka C#:C# 9.0 adds the following features and enhancements to the C# language:

  • RekordyRecords
  • Metody ustawiające tylko do inicjowaniaInit only setters
  • Instrukcje najwyższego poziomuTop-level statements
  • Ulepszenia dopasowania wzorcówPattern matching enhancements
  • Liczby całkowite o wielkości natywnejNative sized integers
  • Wskaźniki funkcjiFunction pointers
  • Pomiń emitowanie flagi localsinitSuppress emitting localsinit flag
  • Nowe wyrażenia z typem docelowymTarget-typed new expressions
  • statyczne funkcje anonimowestatic anonymous functions
  • Wyrażenia warunkowe z typem docelowymTarget-typed conditional expressions
  • Kowariantne typy zwracaneCovariant return types
  • GetEnumeratorObsługa rozszerzeń dla foreach pętliExtension GetEnumerator support for foreach loops
  • Parametry odrzucania wyrażenia lambdaLambda discard parameters
  • Atrybuty funkcji lokalnychAttributes on local functions
  • Inicjatory modułówModule initializers
  • Nowe funkcje dla metod częściowychNew features for partial methods

Język C# 9,0 jest obsługiwany w programie .NET 5.C# 9.0 is supported on .NET 5. Aby uzyskać więcej informacji, zobacz wersja języka C#.For more information, see C# language versioning.

Najnowszą wersję zestawu SDK platformy .NET można pobrać ze strony plików do pobrania platformy .NET.You can download the latest .NET SDK from the .NET downloads page.

Typy rekordówRecord types

W języku C# 9,0 wprowadzono *typy rekordów _, które są typu referencyjnego, który dostarcza metody, które umożliwiają syntezę wartości dla równości.C# 9.0 introduces *record types _, which are a reference type that provides synthesized methods to provide value semantics for equality. Rekordy są domyślnie niezmienne.Records are immutable by default.

Typy rekordów ułatwiają tworzenie niemodyfikowalnych typów referencyjnych w programie .NET.Record types make it easy to create immutable reference types in .NET. Historycznie typy .NET są w znacznym stopniu klasyfikowane jako typy referencyjne (w tym klasy i typy anonimowe) i typy wartości (w tym struktury i krotki).Historically, .NET types are largely classified as reference types (including classes and anonymous types) and value types (including structs and tuples). Chociaż zaleca się niezmienne typy wartości, modyfikowalne typy wartości nie często wprowadzają błędy.While immutable value types are recommended, mutable value types don’t often introduce errors. Zmienne typu wartości przechowują wartości, więc zmiany są wprowadzane do kopii oryginalnych danych, gdy typy wartości są przekazywane do metod.Value type variables hold the values so changes are made to a copy of the original data when value types are passed to methods.

Istnieje wiele zalet, aby również niezmienne typy odwołań.There are many advantages to immutable reference types as well. Te zalety są bardziej wymawiane w współbieżnych programach z udostępnionymi danymi.These advantages are more pronounced in concurrent programs with shared data. Niestety, w języku C# wymuszono napisanie całkiem dowolnego dodatkowego kodu w celu utworzenia niemodyfikowalnych typów referencyjnych.Unfortunately, C# forced you to write quite a bit of extra code to create immutable reference types. Rekordy zapewniają deklarację typu dla niezmiennego typu referencyjnego, który używa semantyki wartości dla równości.Records provide a type declaration for an immutable reference type that uses value semantics for equality. W metodach syntezy dla kodów równości i wyznaczania wartości skrótu należy wziąć pod uwagę dwa rekordy równe, jeśli ich właściwości są równe.The synthesized methods for equality and hash codes consider two records equal if their properties are all equal. Należy wziąć pod uwagę tę definicję:Consider this definition:

public record Person
{
    public string LastName { get; }
    public string FirstName { get; }

    public Person(string first, string last) => (FirstName, LastName) = (first, last);
}

Definicja rekordu tworzy Person Typ, który zawiera dwie właściwości ReadOnly: FirstName i LastName .The record definition creates a Person type that contains two readonly properties: FirstName and LastName. PersonTyp jest typem referencyjnym.The Person type is a reference type. Jeśli szukasz języka IL, jest on klasą.If you looked at the IL, it’s a class. Jest to niezmienne w przypadku, gdy żadna z właściwości nie może być modyfikowana po utworzeniu.It’s immutable in that none of the properties can be modified once it's been created. W przypadku zdefiniowania typu rekordu, kompilator umożliwia wyszukanie kilku innych metod:When you define a record type, the compiler synthesizes several other methods for you:

  • Metody porównania równości opartej na wartościachMethods for value-based equality comparisons
  • Przesłoń dla GetHashCode()Override for GetHashCode()
  • Kopiowanie i klonowanie elementów członkowskichCopy and Clone members
  • PrintMembers i ToString()PrintMembers and ToString()

Rekordy obsługują dziedziczenie.Records support inheritance. Można zadeklarować nowy rekord pochodzący z Person następujących metod:You can declare a new record derived from Person as follows:

public record Teacher : Person
{
    public string Subject { get; }

    public Teacher(string first, string last, string sub)
        : base(first, last) => Subject = sub;
}

Możesz również zapieczętować rekordy, aby zapobiec dalszemu występowaniu:You can also seal records to prevent further derivation:

public sealed record Student : Person
{
    public int Level { get; }

    public Student(string first, string last, int level) : base(first, last) => Level = level;
}

Kompilator syntezuje różne wersje powyższych metod.The compiler synthesizes different versions of the methods above. Sygnatury metod są zależne od tego, czy typ rekordu jest zapieczętowany i czy bezpośrednia klasa bazowa to obiekt.The method signatures depend on if the record type is sealed and if the direct base class is object. Rekordy powinny mieć następujące możliwości:Records should have the following capabilities:

  • Równość jest oparta na wartości i zawiera sprawdzenie, czy typy pasują do siebie.Equality is value-based, and includes a check that the types match. Na przykład wartość Student nie może być równa Person , nawet jeśli dwa rekordy mają tę samą nazwę.For example, a Student can't be equal to a Person, even if the two records share the same name.
  • Rekordy mają wygenerowaną spójną reprezentację ciągu.Records have a consistent string representation generated for you.
  • Rekordy obsługują konstrukcję kopiowania.Records support copy construction. Poprawna konstrukcja kopii musi zawierać hierarchie dziedziczenia i właściwości dodawane przez deweloperów.Correct copy construction must include inheritance hierarchies, and properties added by developers.
  • Rekordy można kopiować z modyfikacją.Records can be copied with modification. Te operacje kopiowania i modyfikowania obsługują mutację nieniszczącą.These copy and modify operations supports non-destructive mutation.

Oprócz znanych Equals przeciążeń, i kompilator umożliwia wypróbowanie operator == operator != nowej EqualityContract właściwości.In addition to the familiar Equals overloads, operator ==, and operator !=, the compiler synthesizes a new EqualityContract property. Właściwość zwraca Type obiekt, który jest zgodny z typem rekordu.The property returns a Type object that matches the type of the record. Jeśli typem podstawowym jest object , właściwość jest virtual .If the base type is object, the property is virtual. Jeśli typ podstawowy jest innym typem rekordu, właściwość jest override .If the base type is another record type, the property is an override. Jeśli typem rekordu jest sealed , właściwość jest sealed .If the record type is sealed, the property is sealed. W syntezie są GetHashCode stosowane GetHashCode wszystkie właściwości i pola zadeklarowane w typie podstawowym oraz typ rekordu.The synthesized GetHashCode uses the GetHashCode from all properties and fields declared in the base type and the record type. Te metody, które zostały opisane w tej metodzie, wymuszają równość opartych na wartościach w hierarchii dziedziczeniaThese synthesized methods enforce value-based equality throughout an inheritance hierarchy. Oznacza to, że Student nigdy nie będzie traktowane jako równe a Person o tej samej nazwie.That means a Student will never be considered equal to a Person with the same name. Typy dwóch rekordów muszą być zgodne oraz wszystkie właściwości, które są współużytkowane przez typy rekordów równe.The types of the two records must match as well as all properties shared among the record types being equal.

Rekordy mają również konstruktory z syntezą i metodę "Clone" służącą do tworzenia kopii.Records also have a synthesized constructor and a "clone" method for creating copies. Konstruktor z syntezą ma jeden parametr typu rekordu.The synthesized constructor has a single parameter of the record type. Tworzy nowy rekord z tymi samymi wartościami dla wszystkich właściwości rekordu.It produces a new record with the same values for all properties of the record. Ten konstruktor jest prywatny, jeśli rekord jest zapieczętowany, w przeciwnym razie jest chroniony.This constructor is private if the record is sealed, otherwise it's protected. Synteza "klonowanie" obsługuje konstrukcję kopiowania dla hierarchii rekordów.The synthesized "clone" method supports copy construction for record hierarchies. Termin "klon" jest w cudzysłowie, ponieważ rzeczywista nazwa jest generowana przez kompilator.The term "clone" is in quotes because the actual name is compiler generated. Nie można utworzyć metody o nazwie Clone w typie rekordu.You can't create a method named Clone in a record type. Synteza "klon" zwraca typ rekordu, który jest kopiowany przy użyciu wysyłki wirtualnej.The synthesized "clone" method returns the type of record being copied using virtual dispatch. Kompilator dodaje różne Modyfikatory dla metody "Clone" w zależności od modyfikatorów dostępu w record :The compiler adds different modifiers for the "clone" method depending on the access modifiers on the record:

  • Jeśli typem rekordu jest abstract , Metoda "klonowanie" jest również abstract .If the record type is abstract, the "clone" method is also abstract. Jeśli typ podstawowy nie jest object , metoda jest również override .If the base type isn't object, the method is also override.
  • Dla typów rekordów, które nie są, abstract gdy typem podstawowym jest object :For record types that aren't abstract when the base type is object:
    • Jeśli rekord ma wartość sealed , żadne dodatkowe Modyfikatory nie są dodawane do metody "klonowania" (oznacza to, że nie jest virtual ).If the record is sealed, no additional modifiers are added to the "clone" method (meaning it is not virtual).
    • Jeśli rekord nie jest sealed , Metoda "Clone" ma wartość virtual .If the record isn't sealed, the "clone" method is virtual.
  • Dla typów rekordów, które nie są, abstract gdy typ podstawowy nie jest object :For record types that aren't abstract when the base type is not object:
    • Jeśli rekordem jest sealed , Metoda "klonowanie" jest również sealed .If the record is sealed, the "clone" method is also sealed.
    • Jeśli rekord nie jest sealed , Metoda "Clone" ma wartość override .If the record isn't sealed, the "clone" method is override.

Wynik wszystkich tych reguł jest implementowany spójnie dla każdej hierarchii typów rekordów.The result of all these rules is the equality is implemented consistently across any hierarchy of record types. Dwa rekordy są równe siebie, jeśli ich właściwości są równe, a ich typy są takie same, jak pokazano w następującym przykładzie:Two records are equal to each other if their properties are equal and their types are the same, as shown in the following example:

var person = new Person("Bill", "Wagner");
var student = new Student("Bill", "Wagner", 11);

Console.WriteLine(student == person); // false

Kompilator syntezuje dwie metody, które obsługują wydruk wyjściowy: ToString() przesłonięcie i PrintMembers .The compiler synthesizes two methods that support printed output: a ToString() override, and PrintMembers. PrintMembersPrzyjmuje System.Text.StringBuilder jako argument.The PrintMembers takes a System.Text.StringBuilder as its argument. Dodaje rozdzieloną przecinkami listę nazw właściwości i wartości dla wszystkich właściwości w typie rekordu.It appends a comma-separated list of property names and values for all properties in the record type. PrintMembers wywołuje podstawową implementację wszystkich rekordów pochodzących z innych rekordów.PrintMembers calls the base implementation for any records derived from other records. ToString()Zastąpienie zwraca ciąg utworzony przez PrintMembers , ujęty w { i } .The ToString() override returns the string produced by PrintMembers, surrounded by { and }. Na przykład ToString() Metoda Student zwraca string podobne do poniższego kodu:For example, the ToString() method for Student returns a string like the following code:

"Student { LastName = Wagner, FirstName = Bill, Level = 11 }"

Przykłady pokazane do użycia tradycyjną składnią do deklarowania właściwości.The examples shown so far use traditional syntax to declare properties. Istnieje bardziej zwięzła forma o nazwie rekordy pozycyjne.There's a more concise form called positional records. Oto trzy typy rekordów zdefiniowane wcześniej jako rekordy pozycyjne:Here are the three record types defined earlier as positional records:

public record Person(string FirstName, string LastName);

public record Teacher(string FirstName, string LastName,
    string Subject)
    : Person(FirstName, LastName);

public sealed record Student(string FirstName,
    string LastName, int Level)
    : Person(FirstName, LastName);

Te deklaracje tworzą takie same funkcje jak wcześniejsza wersja (z kilkoma dodatkowymi funkcjami wymienionymi w poniższej sekcji).These declarations create the same functionality as the earlier version (with a couple extra features covered in the following section). Te deklaracje kończą się średnikiem zamiast nawiasów, ponieważ te rekordy nie dodają dodatkowych metod.These declarations end with a semicolon instead of brackets because these records don't add additional methods. Możesz również dodać treść i dołączyć wszelkie dodatkowe metody:You can add a body, and include any additional methods as well:

public record Pet(string Name)
{
    public void ShredTheFurniture() =>
        Console.WriteLine("Shredding furniture");
}

public record Dog(string Name) : Pet(Name)
{
    public void WagTail() =>
        Console.WriteLine("It's tail wagging time");

    public override string ToString()
    {
        StringBuilder s = new();
        base.PrintMembers(s);
        return $"{s.ToString()} is a dog";
    }
}

Kompilator tworzy Deconstruct metodę dla rekordów pozycyjnych.The compiler produces a Deconstruct method for positional records. DeconstructMetoda ma parametry, które pasują do nazw wszystkich właściwości publicznych w typie rekordu.The Deconstruct method has parameters that match the names of all public properties in the record type. DeconstructMetoda może służyć do dekonstruowania rekordu we właściwościach składnika:The Deconstruct method can be used to deconstruct the record into its component properties:

var person = new Person("Bill", "Wagner");

var (first, last) = person;
Console.WriteLine(first);
Console.WriteLine(last);

Wreszcie rejestruje with wyrażeniaobsługi.Finally, records support with expressions. * with Expression_ _ instruuje kompilator, aby utworzył kopię rekordu, ale _with określone właściwości zmodyfikowano:A *with expression_ _ instructs the compiler to create a copy of a record, but _with specified properties modified:

Person brother = person with { FirstName = "Paul" };

Poprzedni wiersz tworzy nowy Person rekord, w którym LastName Właściwość jest kopią person , a FirstName ma wartość "Paul" .The previous line creates a new Person record where the LastName property is a copy of person, and the FirstName is "Paul". W wyrażeniu można ustawić dowolną liczbę właściwości with .You can set any number of properties in a with expression. Możesz również użyć with wyrażeń, aby utworzyć dokładną kopię.You can also use with expressions to create an exact copy. Należy określić pusty zestaw właściwości do zmodyfikowania:You specify the empty set for the properties to modify:

Person clone = person with { };

Każdy z tych elementów członkowskich, z wyjątkiem metody "Clone", może zostać przez Ciebie zapisany.Any of the synthesized members except the "clone" method may be written by you. Jeśli typ rekordu ma metodę, która pasuje do sygnatury dowolnej metody, kompilator nie wykonuje syntezy tej metody.If a record type has a method that matches the signature of any synthesized method, the compiler doesn't synthesize that method. W przykładzie wcześniejszego Dog rekordu znajduje się ToString() Przykładowa Metoda ze znakiem.The earlier Dog record example contains a hand coded ToString() method as an example.

Dowiedz się więcej na temat typów rekordów w tym samouczku eksploracji rekordów .Learn more about record types in this exploration of records tutorial.

Metody ustawiające tylko do inicjowaniaInit only setters

*Tylko metody init _ mają spójną składnię, aby inicjować elementy członkowskie obiektu.*Init only setters _ provide consistent syntax to initialize members of an object. Inicjatory właściwości sprawiają, że wartość jest ustawiana dla właściwości.Property initializers make it clear which value is setting which property. Minusemem jest to, że te właściwości muszą mieć wartość settable.The downside is that those properties must be settable. Począwszy od języka C# 9,0, można tworzyć metody init dostępu zamiast set metod dostępu do właściwości i indeksatorów.Starting with C# 9.0, you can create init accessors instead of set accessors for properties and indexers. Obiekty wywołujące mogą używać składni inicjatora właściwości do ustawiania tych wartości w wyrażeniach tworzenia, ale te właściwości są tylko do odczytu, gdy konstrukcja została ukończona.Callers can use property initializer syntax to set these values in creation expressions, but those properties are readonly once construction has completed. Tylko metody init umożliwiają zmianę stanu okna.Init only setters provide a window to change state. To okno zostanie zamknięte po zakończeniu fazy budowania.That window closes when the construction phase ends. Faza konstruowania skutecznie kończy działanie po wszystkich inicjalizacjach, w tym inicjatorach właściwości i wyrażeniach z.The construction phase effectively ends after all initialization, including property initializers and with-expressions have completed.

Można zadeklarować init tylko metody ustawiające w dowolnym typie, który napiszesz.You can declare init only setters in any type you write. Na przykład następująca struktura definiuje strukturę obserwacji pogody:For example, the following struct defines a weather observation structure:

public struct WeatherObservation
{
    public DateTime RecordedAt { get; init; }
    public decimal TemperatureInCelsius { get; init; }
    public decimal PressureInMillibars { get; init; }

    public override string ToString() =>
        $"At {RecordedAt:h:mm tt} on {RecordedAt:M/d/yyyy}: " +
        $"Temp = {TemperatureInCelsius}, with {PressureInMillibars} pressure";
}

Obiekty wywołujące mogą używać składni inicjatora właściwości do ustawiania wartości, pozostawiając zachowanie niezmienności:Callers can use property initializer syntax to set the values, while still preserving the immutability:

var now = new WeatherObservation 
{ 
    RecordedAt = DateTime.Now, 
    TemperatureInCelsius = 20, 
    PressureInMillibars = 998.0m 
};

Jednak zmiana obserwacji po inicjacji jest błędem przez przypisanie do właściwości "init-Only" poza inicjalizacją:But, changing an observation after initialization is an error by assigning to an init-only property outside of initialization:

// Error! CS8852.
now.TemperatureInCelsius = 18;

Tylko metody init mogą być przydatne do ustawiania właściwości klasy bazowej z klas pochodnych.Init only setters can be useful to set base class properties from derived classes. Mogą także ustawiać właściwości pochodne za pomocą pomocników w klasie bazowej.They can also set derived properties through helpers in a base class. Rekordy pozycyjne deklarują właściwości przy użyciu tylko metod init.Positional records declare properties using init only setters. Te metody ustawiające są używane w wyrażeniach with.Those setters are used in with-expressions. Można zadeklarować tylko metody init dla dowolnego class lub struct zdefiniowanego elementu.You can declare init only setters for any class or struct you define.

Instrukcje najwyższego poziomuTop-level statements

Instrukcje najwyższego poziomu usuwają niepotrzebne procedury z wielu aplikacji.Top-level statements remove unnecessary ceremony from many applications. Rozważmy kanoniczną "Hello world!"Consider the canonical "Hello World!" Programprogram:

using System;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

Istnieje tylko jeden wiersz kodu, który robi wszystko.There’s only one line of code that does anything. Przy użyciu instrukcji najwyższego poziomu można zastąpić wszystkie te, które są standardową using instrukcją i pojedynczy wiersz, który wykonuje prace:With top-level statements, you can replace all that boilerplate with the using statement and the single line that does the work:

using System;

Console.WriteLine("Hello World!");

Jeśli chciałeś zastosować program jednowierszowy, możesz usunąć using dyrektywę i użyć w pełni kwalifikowanej nazwy typu:If you wanted a one-line program, you could remove the using directive and use the fully qualified type name:

System.Console.WriteLine("Hello World!");

Tylko jeden plik w aplikacji może używać instrukcji najwyższego poziomu.Only one file in your application may use top-level statements. Jeśli kompilator odnajdzie instrukcje najwyższego poziomu w wielu plikach źródłowych, jest to błąd.If the compiler finds top-level statements in multiple source files, it’s an error. Jest również błędem, jeśli łączysz instrukcje najwyższego poziomu z zadeklarowaną metodą punktu wejścia programu, zwykle Main metodę.It’s also an error if you combine top-level statements with a declared program entry point method, typically a Main method. W tym sensie można wziąć pod uwagę, że jeden plik zawiera instrukcje, które zwykle są w Main metodzie Program klasy.In a sense, you can think that one file contains the statements that would normally be in the Main method of a Program class.

Jednym z najpopularniejszych zastosowania tej funkcji jest tworzenie materiałów edukacyjnych.One of the most common uses for this feature is creating teaching materials. Początkująci deweloperzy języka C# mogą pisać kanoniczny "Hello world!"Beginner C# developers can write the canonical “Hello World!” w jednym lub dwóch wierszach kodu.in one or two lines of code. Żadna z dodatkowych procedury nie jest wymagana.None of the extra ceremony is needed. Niemniej deweloperzy mogą również znaleźć wiele użycia tej funkcji.However, seasoned developers will find many uses for this feature as well. W instrukcjach najwyższego poziomu można korzystać ze środowiska podobnego do skryptu na potrzeby eksperymentowania podobnego do tego, co zapewnia notesy Jupyter.Top-level statements enable a script-like experience for experimentation similar to what Jupyter notebooks provide. Instrukcje najwyższego poziomu są doskonałe dla małych programów konsolowych i narzędzi.Top-level statements are great for small console programs and utilities. Azure Functions są idealnym przypadkiem użycia dla instrukcji najwyższego poziomu.Azure Functions are an ideal use case for top-level statements.

Co najważniejsze, instrukcje najwyższego poziomu nie ograniczają zakresu lub złożoności aplikacji.Most importantly, top-level statements don't limit your application’s scope or complexity. Te instrukcje mogą uzyskać dostęp do dowolnej klasy .NET lub korzystać z niej.Those statements can access or use any .NET class. Nie ograniczają one również użycia argumentów wiersza polecenia ani zwracanych wartości.They also don’t limit your use of command-line arguments or return values. Instrukcje najwyższego poziomu mogą uzyskać dostęp do tablicy ciągów o nazwach argumentów.Top-level statements can access an array of strings named args. Jeśli instrukcje najwyższego poziomu zwracają wartość całkowitą, ta wartość zostanie zwróconym kodem zwrotnym z metody z syntezą Main .If the top-level statements return an integer value, that value becomes the integer return code from a synthesized Main method. Instrukcje najwyższego poziomu mogą zawierać wyrażenia asynchroniczne.The top-level statements may contain async expressions. W takim przypadku syntezny punkt wejścia zwraca Task lub Task<int> .In that case, the synthesized entry point returns a Task, or Task<int>.

Ulepszenia dopasowania wzorcówPattern matching enhancements

W języku C# 9 wprowadzono nowe ulepszenia dopasowania do wzorca:C# 9 includes new pattern matching improvements:

  • Wzorce typu zgodne ze zmienną jest typemType patterns match a variable is a type
  • Wzorce w nawiasach wymuszają lub podkreślają pierwszeństwo kombinacji wzorcówParenthesized patterns enforce or emphasize the precedence of pattern combinations
  • *and Wzorce conjunctive_*_ wymagają dopasowania obu wzorcówConjunctive and patterns require both patterns to match
  • *or Wzorce disjunctive_*_ wymagają dopasowania do wzorcaDisjunctive or patterns require either pattern to match
  • *not Wzorce negacji_*_ wymagają, aby wzorzec nie był zgodnyNegated not patterns require that a pattern doesn’t match
  • Wzorce relacyjne wymagają, aby dane wejściowe były mniejsze niż, większe niż lub równe lub większe niż lub równe danej stałej.Relational patterns require the input be less than, greater than, less than or equal, or greater than or equal to a given constant.

Wzorce te wzbogacają składnię wzorców.These patterns enrich the syntax for patterns. Rozważ następujące przykłady:Consider these examples:

public static bool IsLetter(this char c) =>
    c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';

Alternatywnie, z opcjonalnymi nawiasami, aby wyczyścić ten element, który and ma wyższy priorytet niż or :Alternatively, with optional parentheses to make it clear that and has higher precedence than or:

public static bool IsLetterOrSeparator(this char c) =>
    c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',';

Jednym z najpopularniejszych zastosowania jest nowa składnia dla sprawdzania wartości null:One of the most common uses is a new syntax for a null check:

if (e is not null)
{
    // ...
}

Któregokolwiek z tych wzorców można używać w dowolnym kontekście, w którym wzorce są dozwolone: is wyrażenia wzorców, switch wyrażenia, zagnieżdżone wzorce i wzorzec switch case etykiety instrukcji.Any of these patterns can be used in any context where patterns are allowed: is pattern expressions, switch expressions, nested patterns, and the pattern of a switch statement’s case label.

Wydajność i międzyoperacyjnośćPerformance and interop

Trzy nowe funkcje ulepszają obsługę natywnych bibliotek międzyoperacyjnych i niskiego poziomu, które wymagają wysokiej wydajności: natywnych liczb całkowitych, wskaźników funkcji i pomijania localsinit flagi.Three new features improve support for native interop and low-level libraries that require high performance: native sized integers, function pointers, and omitting the localsinit flag.

Liczby całkowite o rozmiarze natywnym nint i nuint są typami całkowitymi.Native sized integers, nint and nuint, are integer types. Są one wyrażone przez typy podstawowe System.IntPtr i System.UIntPtr .They're expressed by the underlying types System.IntPtr and System.UIntPtr. Kompilator wyświetla dodatkowe konwersje i operacje dla tych typów jako natywny liczby całkowite.The compiler surfaces additional conversions and operations for these types as native ints. Liczby całkowite o rozmiarze natywnym definiują właściwości dla MaxValue lub MinValue .Native sized integers define properties for MaxValue or MinValue. Te wartości nie mogą być wyrażone jako stałe czasu kompilacji, ponieważ zależą od natywnego rozmiaru liczby całkowitej na maszynie docelowej.These values can't be expressed as compile time constants because they depend on the native size of an integer on the target machine. Te wartości są tylko do odczytu w czasie wykonywania.Those values are readonly at runtime. Można użyć wartości stałych dla nint zakresu [ int.MinValue ..You can use constant values for nint in the range [int.MinValue .. int.MaxValue].int.MaxValue]. Można użyć wartości stałych dla nuint zakresu [ uint.MinValue ..You can use constant values for nuint in the range [uint.MinValue .. uint.MaxValue].uint.MaxValue]. Kompilator wykonuje stałe łamanie dla wszystkich operatorów jednoargumentowych i binarnych przy użyciu System.Int32 System.UInt32 typów i.The compiler performs constant folding for all unary and binary operators using the System.Int32 and System.UInt32 types. Jeśli wynik nie mieści się w 32 bitów, operacja jest wykonywana w czasie wykonywania i nie jest traktowana jako stała.If the result doesn't fit in 32 bits, the operation is executed at runtime and isn't considered a constant. Natywne liczby całkowite mogą zwiększyć wydajność w scenariuszach, w których liczba obliczeń matematycznych jest szeroko stosowana i musi mieć możliwie najszybszą wydajność.Native sized integers can increase performance in scenarios where integer math is used extensively and needs to have the fastest performance possible.

Wskaźniki funkcji umożliwiają łatwą składnię dostępu do kodów opcode IL ldftn i calli .Function pointers provide an easy syntax to access the IL opcodes ldftn and calli. Można zadeklarować wskaźniki funkcji przy użyciu nowej delegate_ składni.You can declare function pointers using new delegate_ syntax. delegate*Typ jest typem wskaźnika.A delegate* type is a pointer type. Wywoływanie delegate* użycia typu calli , w przeciwieństwie do delegata, który używa callvirt Invoke() metody.Invoking the delegate* type uses calli, in contrast to a delegate that uses callvirt on the Invoke() method. Syntaktycznie wywołania są identyczne.Syntactically, the invocations are identical. Wywołanie wskaźnika funkcji używa managed konwencji wywoływania.Function pointer invocation uses the managed calling convention. Dodaj unmanaged słowo kluczowe po delegate* składni, aby zadeklarować, że chcesz unmanaged konwencję wywoływania.You add the unmanaged keyword after the delegate* syntax to declare that you want the unmanaged calling convention. Inne konwencje wywoływania można określić przy użyciu atrybutów w delegate* deklaracji.Other calling conventions can be specified using attributes on the delegate* declaration.

Na koniec możesz dodać polecenie, System.Runtime.CompilerServices.SkipLocalsInitAttribute aby poinstruować kompilator, aby nie emitują localsinit flagi.Finally, you can add the System.Runtime.CompilerServices.SkipLocalsInitAttribute to instruct the compiler not to emit the localsinit flag. Ta flaga instruuje CLR, aby nie zainicjowano wszystkich zmiennych lokalnych.This flag instructs the CLR to zero-initialize all local variables. localsinitFlaga była domyślnym zachowaniem języka C# od 1,0.The localsinit flag has been the default behavior for C# since 1.0. Jednak dodatkowe zero inicjacji może mieć wymierny wpływ na wydajność w niektórych scenariuszach.However, the extra zero-initialization may have measurable performance impact in some scenarios. W szczególności w przypadku korzystania z programu stackalloc .In particular, when you use stackalloc. W takich przypadkach można dodać SkipLocalsInitAttribute .In those cases, you can add the SkipLocalsInitAttribute. Możesz dodać go do pojedynczej metody lub właściwości, lub do class modułu,, struct , interface lub nawet.You may add it to a single method or property, or to a class, struct, interface, or even a module. Ten atrybut nie ma wpływu na abstract metody; wpływa na kod wygenerowany dla implementacji.This attribute doesn't affect abstract methods; it affects the code generated for the implementation.

Te funkcje mogą zwiększyć wydajność w niektórych scenariuszach.These features can improve performance in some scenarios. Powinny być używane tylko po dokładnym przeprowadzeniu testu porównawczego zarówno przed, jak i po przyjęciu.They should be used only after careful benchmarking both before and after adoption. Kod obejmujący liczby całkowite o liczbie natywnej musi być testowany na wielu platformach docelowych z różnymi rozmiarami całkowitymi.Code involving native sized integers must be tested on multiple target platforms with different integer sizes. Inne funkcje wymagają niebezpiecznego kodu.The other features require unsafe code.

Funkcje dopasowywania i kończeniaFit and finish features

Wiele innych funkcji ułatwia bardziej wydajne pisanie kodu.Many of the other features help you write code more efficiently. W języku C# 9,0 można pominąć typ w new wyrażeniu , gdy typ tworzonego obiektu jest już znany.In C# 9.0, you can omit the type in a new expression when the created object's type is already known. Najbardziej typowym zastosowaniem jest deklaracja pól:The most common use is in field declarations:

private List<WeatherObservation> _observations = new();

Typ docelowy new może być również używany, gdy trzeba utworzyć nowy obiekt do przekazania jako argument do metody.Target-typed new can also be used when you need to create a new object to pass as an argument to a method. Rozważmy ForecastFor() metodę o następującym podpisie:Consider a ForecastFor() method with the following signature:

public WeatherForecast ForecastFor(DateTime forecastDate, WeatherForecastOptions options)

Można wywołać go w następujący sposób:You could call it as follows:

var forecast = station.ForecastFor(DateTime.Now.AddDays(2), new());

Innym świetnym użyciem tej funkcji jest połączenie z właściwościami tylko init w celu zainicjowania nowego obiektu:Another nice use for this feature is to combine it with init only properties to initialize a new object:

WeatherStation station = new() { Location = "Seattle, WA" };

Można zwrócić wystąpienie utworzone przez konstruktora domyślnego przy użyciu return new(); instrukcji.You can return an instance created by the default constructor using a return new(); statement.

Podobna funkcja ulepsza rozpoznawanie typu docelowego wyrażeń warunkowych.A similar feature improves the target type resolution of conditional expressions. W przypadku tej zmiany dwa wyrażenia nie muszą mieć jawnej konwersji z jednego do drugiego, ale mogą jednocześnie mieć niejawne konwersje na typ docelowy.With this change, the two expressions need not have an implicit conversion from one to the other, but may both have implicit conversions to a target type. Ta zmiana jest niezauważalna.You likely won’t notice this change. Informacje o tym, że niektóre wyrażenia warunkowe, które wcześniej wymagały rzutowania lub nie spowodują skompilowania, już teraz działają.What you will notice is that some conditional expressions that previously required casts or wouldn’t compile now just work.

Począwszy od języka C# 9,0, można dodać static modyfikator do wyrażeń lambda lub metod anonimowych.Starting in C# 9.0, you can add the static modifier to lambda expressions or anonymous methods. Statyczne wyrażenia lambda są analogiczne do static funkcji lokalnych: statyczna metoda lambda lub anonimowa nie może przechwycić lokalnych zmiennych lub stanu wystąpienia.Static lambda expressions are analogous to the static local functions: a static lambda or anonymous method can't capture local variables or instance state. staticModyfikator zapobiega przypadkowemu przechwyceniu innych zmiennych.The static modifier prevents accidentally capturing other variables.

Typy zwracane przez współwarianty zapewniają elastyczność dla zwracanych typów metod zastąpień .Covariant return types provide flexibility for the return types of override methods. Metoda przesłaniania może zwracać typ pochodzący od typu zwracanego przesłoniętej metody bazowej.An override method can return a type derived from the return type of the overridden base method. Może to być przydatne w przypadku rekordów i dla innych typów, które obsługują wirtualne klony lub metody fabryki.This can be useful for records and for other types that support virtual clone or factory methods.

Ponadto foreach Pętla rozpoznaje i używa metody rozszerzenia GetEnumerator , która w przeciwnym razie spełnia warunki foreach wzorca.In addition, the foreach loop will recognize and use an extension method GetEnumerator that otherwise satisfies the foreach pattern. Ta zmiana oznacza foreach spójność z innymi konstrukcjami opartymi na wzorcu, takimi jak wzorzec asynchroniczny i dekonstrukcja oparta na wzorcu.This change means foreach is consistent with other pattern-based constructions such as the async pattern, and pattern-based deconstruction. W tym przypadku ta zmiana oznacza, że można dodać foreach obsługę dowolnego typu.In practice, this change means you can add foreach support to any type. Należy ograniczyć jego użycie do momentu, gdy Wyliczenie obiektu ma sens w projekcie.You should limit its use to when enumerating an object makes sense in your design.

Następnie można użyć odrzutów jako parametrów w wyrażeniach lambda.Next, you can use discards as parameters to lambda expressions. Ta wygoda pozwala uniknąć nazywania argumentu, a kompilator może uniknąć korzystania z niego.This convenience enables you to avoid naming the argument, and the compiler may avoid using it. Używasz _ argumentu for.You use the _ for any argument. Aby uzyskać więcej informacji, zobacz Parametry wejściowe sekcji wyrażenie lambda w artykule wyrażenia lambda .For more information, see the Input parameters of a lambda expression section of the Lambda expressions article.

Na koniec możesz teraz zastosować atrybuty do funkcji lokalnych.Finally, you can now apply attributes to local functions. Na przykład można zastosować Adnotacje atrybutu nullable do funkcji lokalnych.For example, you can apply nullable attribute annotations to local functions.

Obsługa generatorów koduSupport for code generators

Dwie funkcje końcowe obsługują generatory kodu w języku C#.Two final features support C# code generators. Generatory kodu w języku C# to składnik, który można napisać, podobnie jak Analizator Roslyn lub poprawka kodu.C# code generators are a component you can write that is similar to a roslyn analyzer or code fix. Różnica polega na tym, że generatory kodu analizują kod i zapisują nowe pliki kodu źródłowego w ramach procesu kompilacji.The difference is that code generators analyze code and write new source code files as part of the compilation process. Typowy generator kodu wyszukuje kod dla atrybutów lub innych konwencji.A typical code generator searches code for attributes or other conventions.

Generator kodu odczytuje atrybuty lub inne elementy kodu przy użyciu interfejsów API analizy Roslyn.A code generator reads attributes or other code elements using the Roslyn analysis APIs. Z tych informacji dodaje nowy kod do kompilacji.From that information, it adds new code to the compilation. Generatory źródła mogą jedynie dodawać kod; nie mogą modyfikować żadnego istniejącego kodu w kompilacji.Source generators can only add code; they aren't allowed to modify any existing code in the compilation.

Dwie funkcje, które zostały dodane dla generatorów kodu, to rozszerzenia Składnia metody częściowej _ i _inicjatory modułów*.The two features added for code generators are extensions to partial method syntax _, and _module initializers*. Najpierw zmiany w metodach częściowych.First, the changes to partial methods. Przed C# 9,0 metody częściowe są, private ale nie można określić modyfikatora dostępu, mają void Return i nie mogą mieć out parametrów.Before C# 9.0, partial methods are private but can't specify an access modifier, have a void return, and can't have out parameters. Te ograniczenia mające na celu, że jeśli nie zostanie podana implementacja metody, kompilator usuwa wszystkie wywołania metody częściowej.These restrictions meant that if no method implementation is provided, the compiler removes all calls to the partial method. Język C# 9,0 usuwa te ograniczenia, ale wymaga, aby częściowa deklaracja metod była implementacją.C# 9.0 removes these restrictions, but requires that partial method declarations have an implementation. Generatory kodu mogą zapewnić, że implementacja.Code generators can provide that implementation. Aby uniknąć wprowadzenia zmiany, kompilator traktuje każdą metodę częściową bez modyfikatora dostępu, aby przestrzegać starych reguł.To avoid introducing a breaking change, the compiler considers any partial method without an access modifier to follow the old rules. Jeśli metoda częściowa zawiera private modyfikator dostępu, nowe reguły regulują tę metodę częściową.If the partial method includes the private access modifier, the new rules govern that partial method.

Druga Nowa funkcja dla generatorów kodu jest _ inicjatorów modułów *.The second new feature for code generators is _*module initializers**. Inicjatory modułów to metody, które mają ModuleInitializerAttribute dołączony atrybut.Module initializers are methods that have the ModuleInitializerAttribute attribute attached to them. Te metody będą wywoływane przez środowisko uruchomieniowe przed jakimkolwiek innym dostępem do pola lub wywołaniem metody w całym module.These methods will be called by the runtime before any other field access or method invocation within the entire module. Metoda inicjatora modułu:A module initializer method:

  • Musi być statycznaMust be static
  • Musi być bez parametrówMust be parameterless
  • Musi zwracać typ voidMust return void
  • Nie może być metodą generycznąMust not be a generic method
  • Nie może być zawarta w klasie generycznejMust not be contained in a generic class
  • Musi być dostępny z poziomu zawierającego go modułuMust be accessible from the containing module

Ostatni punkt Bullet efektywnie oznacza, że metoda i jej Klasa zawierająca muszą być wewnętrzne lub publiczne.That last bullet point effectively means the method and its containing class must be internal or public. Metoda nie może być funkcją lokalną.The method can't be a local function.