Novidades do C# 9.0What's new in C# 9.0

O c# 9,0 adiciona os seguintes recursos e aprimoramentos à linguagem C#:C# 9.0 adds the following features and enhancements to the C# language:

  • RegistrosRecords
  • Setters somente initInit only setters
  • Instruções de nível superiorTop-level statements
  • Melhorias na correspondência de padrõesPattern matching enhancements
  • Inteiros de tamanho nativoNative sized integers
  • Ponteiros de funçãoFunction pointers
  • Suprimir emissão do sinalizador localsinitSuppress emitting localsinit flag
  • Expressões new com tipo de destinoTarget-typed new expressions
  • funções anônimas estáticasstatic anonymous functions
  • Expressões condicionais de tipo de destinoTarget-typed conditional expressions
  • Tipos de retorno covariantesCovariant return types
  • GetEnumeratorSuporte de extensão para foreach loopsExtension GetEnumerator support for foreach loops
  • Parâmetros discard de lambdaLambda discard parameters
  • Atributos em funções locaisAttributes on local functions
  • Inicializadores de móduloModule initializers
  • Novos recursos para métodos parciaisNew features for partial methods

O C# 9,0 tem suporte no .NET 5.C# 9.0 is supported on .NET 5. Para obter mais informações, consulte controle de versão da linguagem C#.For more information, see C# language versioning.

Você pode baixar o SDK do .NET mais recente na página de downloads do .net.You can download the latest .NET SDK from the .NET downloads page.

Tipos de registroRecord types

O C# 9,0 introduz *tipos de registro _, que são um tipo de referência que fornece métodos sintetizados para fornecer a semântica de valor para igualdade.C# 9.0 introduces *record types _, which are a reference type that provides synthesized methods to provide value semantics for equality. Os registros são imutáveis por padrão.Records are immutable by default.

Os tipos de registro facilitam a criação de tipos de referência imutáveis no .NET.Record types make it easy to create immutable reference types in .NET. Historicamente, os tipos .NET são classificados em grande parte como tipos de referência (incluindo classes e tipos anônimos) e tipos de valor (incluindo structs e tuplas).Historically, .NET types are largely classified as reference types (including classes and anonymous types) and value types (including structs and tuples). Embora tipos de valor imutáveis sejam recomendados, os tipos de valores mutáveis geralmente não introduzem erros.While immutable value types are recommended, mutable value types don’t often introduce errors. As variáveis de tipo de valor contêm os valores para que as alterações sejam feitas em uma cópia dos dados originais quando os tipos de valor são passados para métodos.Value type variables hold the values so changes are made to a copy of the original data when value types are passed to methods.

Também há muitas vantagens em tipos de referência imutáveis.There are many advantages to immutable reference types as well. Essas vantagens são mais pronunciadas em programas simultâneos com dados compartilhados.These advantages are more pronounced in concurrent programs with shared data. Infelizmente, o C# forçou você a escrever um pouco de código extra para criar tipos de referência imutáveis.Unfortunately, C# forced you to write quite a bit of extra code to create immutable reference types. Os registros fornecem uma declaração de tipo para um tipo de referência imutável que usa semântica de valor para igualdade.Records provide a type declaration for an immutable reference type that uses value semantics for equality. Os métodos sintetizados para códigos de igualdade e hash consideram dois registros iguais se suas propriedades forem iguais.The synthesized methods for equality and hash codes consider two records equal if their properties are all equal. Considere esta definição:Consider this definition:

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

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

A definição de registro cria um Person tipo que contém duas propriedades ReadOnly: FirstName e LastName .The record definition creates a Person type that contains two readonly properties: FirstName and LastName. O Person tipo é um tipo de referência.The Person type is a reference type. Se você examinou o IL, ele é uma classe.If you looked at the IL, it’s a class. É imutável, pois nenhuma das propriedades pode ser modificada depois de ser criada.It’s immutable in that none of the properties can be modified once it's been created. Quando você define um tipo de registro, o compilador sintetiza vários outros métodos para você:When you define a record type, the compiler synthesizes several other methods for you:

  • Métodos para comparações de igualdade com base em valorMethods for value-based equality comparisons
  • Substituir para GetHashCode()Override for GetHashCode()
  • Copiar e clonar MembrosCopy and Clone members
  • PrintMembers e ToString()PrintMembers and ToString()

Registra o suporte à herança.Records support inheritance. Você pode declarar um novo registro derivado do da Person seguinte maneira: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;
}

Você também pode lacrar registros para evitar uma maior derivação: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;
}

O compilador sintetiza versões diferentes dos métodos acima.The compiler synthesizes different versions of the methods above. As assinaturas de método dependem de se o tipo de registro é lacrado e se a classe base direta é Object.The method signatures depend on if the record type is sealed and if the direct base class is object. Os registros devem ter os seguintes recursos:Records should have the following capabilities:

  • A igualdade é baseada em valor e inclui uma verificação de que os tipos correspondem.Equality is value-based, and includes a check that the types match. Por exemplo, um Student não pode ser igual a a Person , mesmo que os dois registros compartilhem o mesmo nome.For example, a Student can't be equal to a Person, even if the two records share the same name.
  • Os registros têm uma representação de cadeia de caracteres consistente gerada para você.Records have a consistent string representation generated for you.
  • Os registros dão suporte à construção de cópia.Records support copy construction. A construção correta da cópia deve incluir hierarquias de herança e propriedades adicionadas por desenvolvedores.Correct copy construction must include inheritance hierarchies, and properties added by developers.
  • Os registros podem ser copiados com modificações.Records can be copied with modification. Essas operações de cópia e modificação oferecem suporte a mutação não destrutiva.These copy and modify operations supports non-destructive mutation.

Além das Equals sobrecargas conhecidas, operator == e operator != , o compilador sintetiza uma nova EqualityContract propriedade.In addition to the familiar Equals overloads, operator ==, and operator !=, the compiler synthesizes a new EqualityContract property. A propriedade retorna um Type objeto que corresponde ao tipo do registro.The property returns a Type object that matches the type of the record. Se o tipo base for object , a propriedade será virtual .If the base type is object, the property is virtual. Se o tipo base for outro tipo de registro, a propriedade será um override .If the base type is another record type, the property is an override. Se o tipo de registro for sealed , a propriedade será sealed .If the record type is sealed, the property is sealed. O sintetizado GetHashCode usa o GetHashCode de todas as propriedades e campos declarados no tipo base e no tipo de registro.The synthesized GetHashCode uses the GetHashCode from all properties and fields declared in the base type and the record type. Esses métodos sintetizados impõem a igualdade baseada em valor em uma hierarquia de herança.These synthesized methods enforce value-based equality throughout an inheritance hierarchy. Isso significa que um Student nunca será considerado igual a um Person com o mesmo nome.That means a Student will never be considered equal to a Person with the same name. Os tipos dos dois registros devem corresponder e todas as propriedades compartilhadas entre os tipos de registro são iguais.The types of the two records must match as well as all properties shared among the record types being equal.

Os registros também têm um Construtor sintetizado e um método de "clonagem" para a criação de cópias.Records also have a synthesized constructor and a "clone" method for creating copies. O Construtor sintetizado tem um único parâmetro do tipo de registro.The synthesized constructor has a single parameter of the record type. Ele produz um novo registro com os mesmos valores para todas as propriedades do registro.It produces a new record with the same values for all properties of the record. Esse construtor é privado se o registro estiver lacrado, caso contrário, será protegido.This constructor is private if the record is sealed, otherwise it's protected. O método "clone" sintetizado dá suporte à construção de cópia para hierarquias de registro.The synthesized "clone" method supports copy construction for record hierarchies. O termo "clone" está entre aspas porque o nome real é gerado pelo compilador.The term "clone" is in quotes because the actual name is compiler generated. Você não pode criar um método chamado Clone em um tipo de registro.You can't create a method named Clone in a record type. O método "clone" sintetizado retorna o tipo de registro que está sendo copiado usando a expedição virtual.The synthesized "clone" method returns the type of record being copied using virtual dispatch. O compilador adiciona diferentes modificadores para o método "clone" dependendo dos modificadores de acesso no record :The compiler adds different modifiers for the "clone" method depending on the access modifiers on the record:

  • Se o tipo de registro for abstract , o método "clone" também será abstract .If the record type is abstract, the "clone" method is also abstract. Se o tipo base não for object , o método também será override .If the base type isn't object, the method is also override.
  • Para tipos de registro que não são abstract quando o tipo base é object :For record types that aren't abstract when the base type is object:
    • Se o registro for sealed , nenhum modificador adicional será adicionado ao método "clone" (o que significa que ele não é virtual ).If the record is sealed, no additional modifiers are added to the "clone" method (meaning it is not virtual).
    • Se o registro não for sealed , o método "clone" será virtual .If the record isn't sealed, the "clone" method is virtual.
  • Para tipos de registro que não são abstract quando o tipo base não é object :For record types that aren't abstract when the base type is not object:
    • Se o registro for sealed , o método "clone" também será sealed .If the record is sealed, the "clone" method is also sealed.
    • Se o registro não for sealed , o método "clone" será override .If the record isn't sealed, the "clone" method is override.

O resultado de todas essas regras é que a igualdade é implementada consistentemente em qualquer hierarquia de tipos de registro.The result of all these rules is the equality is implemented consistently across any hierarchy of record types. Dois registros são iguais entre si se suas propriedades forem iguais e seus tipos forem os mesmos, conforme mostrado no exemplo a seguir: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

O compilador sintetiza dois métodos que dão suporte à saída impressa: uma ToString() substituição e PrintMembers .The compiler synthesizes two methods that support printed output: a ToString() override, and PrintMembers. O PrintMembers usa System.Text.StringBuilder como argumento.The PrintMembers takes a System.Text.StringBuilder as its argument. Ele acrescenta uma lista separada por vírgulas de nomes de propriedade e valores para todas as propriedades no tipo de registro.It appends a comma-separated list of property names and values for all properties in the record type. PrintMembers chama a implementação de base para todos os registros derivados de outros registros.PrintMembers calls the base implementation for any records derived from other records. A ToString() substituição retorna a cadeia de caracteres produzida por PrintMembers , cercada por { e } .The ToString() override returns the string produced by PrintMembers, surrounded by { and }. Por exemplo, o ToString() método para Student retorna um string semelhante ao seguinte código:For example, the ToString() method for Student returns a string like the following code:

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

Os exemplos mostrados até agora usam a sintaxe tradicional para declarar Propriedades.The examples shown so far use traditional syntax to declare properties. Há uma forma mais concisa chamada registros posicionais.There's a more concise form called positional records. Aqui estão os três tipos de registro definidos anteriormente como registros posicionais: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);

Essas declarações criam a mesma funcionalidade da versão anterior (com alguns recursos extras abordados na seção a seguir).These declarations create the same functionality as the earlier version (with a couple extra features covered in the following section). Essas declarações terminam com um ponto e vírgula em vez de colchetes porque esses registros não adicionam outros métodos.These declarations end with a semicolon instead of brackets because these records don't add additional methods. Você pode adicionar um corpo e também incluir outros métodos: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";
    }
}

O compilador produz um Deconstruct método para registros posicionais.The compiler produces a Deconstruct method for positional records. O Deconstruct método tem parâmetros que correspondem aos nomes de todas as propriedades públicas no tipo de registro.The Deconstruct method has parameters that match the names of all public properties in the record type. O Deconstruct método pode ser usado para desconstruir o registro em suas propriedades de componente: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);

Por fim, o registra as with expressõesde suporte.Finally, records support with expressions. Um * with expression_ _ instrui o compilador a criar uma cópia de um registro, mas _with propriedades especificadas modificadas:A *with expression_ _ instructs the compiler to create a copy of a record, but _with specified properties modified:

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

A linha anterior cria um novo Person registro no qual a LastName propriedade é uma cópia de person e o FirstName é "Paul" .The previous line creates a new Person record where the LastName property is a copy of person, and the FirstName is "Paul". Você pode definir qualquer número de propriedades em uma with expressão.You can set any number of properties in a with expression. Você também pode usar with expressões para criar uma cópia exata.You can also use with expressions to create an exact copy. Especifique o conjunto vazio para as propriedades a serem modificadas:You specify the empty set for the properties to modify:

Person clone = person with { };

Qualquer um dos membros sintetizados, exceto o método "clone", pode ser escrito por você.Any of the synthesized members except the "clone" method may be written by you. Se um tipo de registro tiver um método que corresponda à assinatura de qualquer método sintetizado, o compilador não sintetizará esse método.If a record type has a method that matches the signature of any synthesized method, the compiler doesn't synthesize that method. O Dog exemplo de registro anterior contém um método codificado ToString() por mão como um exemplo.The earlier Dog record example contains a hand coded ToString() method as an example.

Saiba mais sobre os tipos de registro neste tutorial de exploração de registros .Learn more about record types in this exploration of records tutorial.

Setters somente initInit only setters

*Init somente setters _ fornecem sintaxe consistente para inicializar membros de um objeto.*Init only setters _ provide consistent syntax to initialize members of an object. Os inicializadores de propriedade tornam claro qual valor está definindo qual propriedade.Property initializers make it clear which value is setting which property. A desvantagem é que essas propriedades devem ser configurável.The downside is that those properties must be settable. A partir do C# 9,0, você pode criar init acessadores em vez de set acessadores para propriedades e indexadores.Starting with C# 9.0, you can create init accessors instead of set accessors for properties and indexers. Os chamadores podem usar a sintaxe do inicializador de propriedade para definir esses valores em expressões de criação, mas essas propriedades são ReadOnly quando a construção é concluída.Callers can use property initializer syntax to set these values in creation expressions, but those properties are readonly once construction has completed. Os setters somente init fornecem uma janela para alterar o estado.Init only setters provide a window to change state. Essa janela fecha quando a fase de construção termina.That window closes when the construction phase ends. A fase de construção termina com eficiência após toda a inicialização, incluindo inicializadores de propriedade e with-Expressions concluídos.The construction phase effectively ends after all initialization, including property initializers and with-expressions have completed.

Você pode declarar init somente setters em qualquer tipo que escrever.You can declare init only setters in any type you write. Por exemplo, a seguinte estrutura define uma estrutura de observação do clima: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";
}

Os chamadores podem usar a sintaxe do inicializador de propriedade para definir os valores, enquanto ainda preservam a imutabilidade: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 
};

Mas, a alteração de uma observação após a inicialização é um erro ao atribuir a uma propriedade somente init fora da inicialização:But, changing an observation after initialization is an error by assigning to an init-only property outside of initialization:

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

Os setters somente init podem ser úteis para definir propriedades de classe base de classes derivadas.Init only setters can be useful to set base class properties from derived classes. Eles também podem definir propriedades derivadas por meio de auxiliares em uma classe base.They can also set derived properties through helpers in a base class. Registros posicionais declaram propriedades usando somente init setters.Positional records declare properties using init only setters. Esses setters são usados em with-Expressions.Those setters are used in with-expressions. Você pode declarar setters somente init para qualquer um class ou struct definir.You can declare init only setters for any class or struct you define.

Instruções de nível superiorTop-level statements

As instruções de nível superior removem a cerimônia desnecessária de muitos aplicativos.Top-level statements remove unnecessary ceremony from many applications. Considere o canônico "Olá, Mundo!"Consider the canonical "Hello World!" Programprogram:

using System;

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

Há apenas uma linha de código que faz qualquer coisa.There’s only one line of code that does anything. Com as instruções de nível superior, você pode substituir todo o texto clichê pela using instrução e a única linha que faz o trabalho: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!");

Se você quisesse um programa de uma linha, poderia remover a using diretiva e usar o nome do tipo totalmente qualificado: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!");

Somente um arquivo em seu aplicativo pode usar instruções de nível superior.Only one file in your application may use top-level statements. Se o compilador encontrar instruções de nível superior em vários arquivos de origem, será um erro.If the compiler finds top-level statements in multiple source files, it’s an error. Também será um erro se você combinar instruções de nível superior com um método de ponto de entrada de programa declarado, normalmente um Main método.It’s also an error if you combine top-level statements with a declared program entry point method, typically a Main method. De certa forma, você pode imaginar que um arquivo contém as instruções que normalmente estaria no Main método de uma Program classe.In a sense, you can think that one file contains the statements that would normally be in the Main method of a Program class.

Um dos usos mais comuns para esse recurso é a criação de materiais de ensino.One of the most common uses for this feature is creating teaching materials. Desenvolvedores de C# iniciantes podem escrever o "Olá, Mundo!" canônicoBeginner C# developers can write the canonical “Hello World!” em uma ou duas linhas de código.in one or two lines of code. Nenhuma das cerimônias extras é necessária.None of the extra ceremony is needed. No entanto, os desenvolvedores experientes também encontrarão muitos usos para esse recurso.However, seasoned developers will find many uses for this feature as well. As instruções de nível superior permitem uma experiência semelhante a um script para experimentação semelhante ao que os notebooks Jupyter fornecem.Top-level statements enable a script-like experience for experimentation similar to what Jupyter notebooks provide. As instruções de nível superior são ótimas para pequenos programas de console e utilitários.Top-level statements are great for small console programs and utilities. Azure Functions são um caso de uso ideal para instruções de nível superior.Azure Functions are an ideal use case for top-level statements.

O mais importante é que as instruções de nível superior não limitam o escopo ou a complexidade do aplicativo.Most importantly, top-level statements don't limit your application’s scope or complexity. Essas instruções podem acessar ou usar qualquer classe .NET.Those statements can access or use any .NET class. Eles também não limitam o uso de argumentos de linha de comando ou valores de retorno.They also don’t limit your use of command-line arguments or return values. Instruções de nível superior podem acessar uma matriz de cadeias de caracteres denominadas args.Top-level statements can access an array of strings named args. Se as instruções de nível superior retornarem um valor inteiro, esse valor se tornará o código de retorno de inteiro de um método sintetizado Main .If the top-level statements return an integer value, that value becomes the integer return code from a synthesized Main method. As instruções de nível superior podem conter expressões assíncronas.The top-level statements may contain async expressions. Nesse caso, o ponto de entrada sintetizado retorna um Task , ou Task<int> .In that case, the synthesized entry point returns a Task, or Task<int>.

Melhorias na correspondência de padrõesPattern matching enhancements

O C# 9 inclui novas melhorias de correspondência de padrões:C# 9 includes new pattern matching improvements:

  • Padrões de tipo correspondem a uma variável é um tipoType patterns match a variable is a type
  • Padrões entre parênteses impõem ou enfatizam a precedência de combinações de padrõesParenthesized patterns enforce or emphasize the precedence of pattern combinations
  • *and Padrões de conjuntiva_*_ exigem os dois padrões para corresponderConjunctive and patterns require both patterns to match
  • *or Padrões de disjunctive_*_ exigem qualquer padrão para corresponderDisjunctive or patterns require either pattern to match
  • *not Padrões negados_*_ exigem que um padrão não correspondaNegated not patterns require that a pattern doesn’t match
  • Os padrões relacionais exigem que a entrada seja menor que, maior que, menor ou igual ou maior ou igual a uma determinada constante.Relational patterns require the input be less than, greater than, less than or equal, or greater than or equal to a given constant.

Esses padrões enriquecem a sintaxe para padrões.These patterns enrich the syntax for patterns. Considere estes exemplos:Consider these examples:

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

Como alternativa, com parênteses opcionais para deixá-lo claro que and tem precedência maior do que 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 ',';

Um dos usos mais comuns é uma nova sintaxe para uma verificação nula:One of the most common uses is a new syntax for a null check:

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

Qualquer um desses padrões pode ser usado em qualquer contexto em que os padrões são permitidos: is expressões switch de padrão, expressões, padrões aninhados e o padrão do rótulo de uma switch instrução case .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.

Desempenho e interoperabilidadePerformance and interop

Três novos recursos melhoram o suporte para a interoperabilidade nativa e bibliotecas de nível baixo que exigem alto desempenho: inteiros de tamanho nativo, ponteiros de função e omissão do localsinit sinalizador.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.

Inteiros de tamanho nativo nint e nuint , são tipos inteiros.Native sized integers, nint and nuint, are integer types. Eles são expressos pelos tipos subjacentes System.IntPtr e System.UIntPtr .They're expressed by the underlying types System.IntPtr and System.UIntPtr. O compilador superfícies de conversões e operações adicionais para esses tipos como ints nativas.The compiler surfaces additional conversions and operations for these types as native ints. Os inteiros de tamanho nativo definem propriedades para MaxValue ou MinValue .Native sized integers define properties for MaxValue or MinValue. Esses valores não podem ser expressos como constantes de tempo de compilação porque dependem do tamanho nativo de um inteiro no computador de destino.These values can't be expressed as compile time constants because they depend on the native size of an integer on the target machine. Esses valores são ReadOnly no tempo de execução.Those values are readonly at runtime. Você pode usar valores constantes para nint no intervalo [ int.MinValue ..You can use constant values for nint in the range [int.MinValue .. int.MaxValue].int.MaxValue]. Você pode usar valores constantes para nuint no intervalo [ uint.MinValue ..You can use constant values for nuint in the range [uint.MinValue .. uint.MaxValue].uint.MaxValue]. O compilador executa o dobramento constante para todos os operadores unários e binários usando os System.Int32 System.UInt32 tipos e.The compiler performs constant folding for all unary and binary operators using the System.Int32 and System.UInt32 types. Se o resultado não couber em 32 bits, a operação será executada em tempo de execução e não será considerada uma constante.If the result doesn't fit in 32 bits, the operation is executed at runtime and isn't considered a constant. Os inteiros de tamanho nativo podem aumentar o desempenho em cenários em que a matemática de inteiros é usada extensivamente e precisa ter o desempenho mais rápido possível.Native sized integers can increase performance in scenarios where integer math is used extensively and needs to have the fastest performance possible.

Ponteiros de função fornecem uma sintaxe fácil para acessar os opcodes de IL ldftn e calli .Function pointers provide an easy syntax to access the IL opcodes ldftn and calli. Você pode declarar ponteiros de função usando a nova delegate_ sintaxe.You can declare function pointers using new delegate_ syntax. Um delegate* tipo é um tipo de ponteiro.A delegate* type is a pointer type. Invocar o delegate* tipo usa calli , em contraste com um delegado que usa callvirt no Invoke() método.Invoking the delegate* type uses calli, in contrast to a delegate that uses callvirt on the Invoke() method. Sintaticamente, as invocações são idênticas.Syntactically, the invocations are identical. Invocação de ponteiro de função usa a managed Convenção de chamada.Function pointer invocation uses the managed calling convention. Você adiciona a unmanaged palavra-chave após a delegate* sintaxe para declarar que deseja a unmanaged Convenção de chamada.You add the unmanaged keyword after the delegate* syntax to declare that you want the unmanaged calling convention. Outras convenções de chamada podem ser especificadas usando atributos na delegate* declaração.Other calling conventions can be specified using attributes on the delegate* declaration.

Por fim, você pode adicionar o System.Runtime.CompilerServices.SkipLocalsInitAttribute para instruir o compilador a não emitir o localsinit sinalizador.Finally, you can add the System.Runtime.CompilerServices.SkipLocalsInitAttribute to instruct the compiler not to emit the localsinit flag. Esse sinalizador instrui o CLR a inicializar zero todas as variáveis locais.This flag instructs the CLR to zero-initialize all local variables. O localsinit sinalizador tem sido o comportamento padrão para C# desde 1,0.The localsinit flag has been the default behavior for C# since 1.0. No entanto, a inicialização zero extra pode ter um impacto mensurável no desempenho em alguns cenários.However, the extra zero-initialization may have measurable performance impact in some scenarios. Em particular, quando você usa o stackalloc .In particular, when you use stackalloc. Nesses casos, você pode adicionar o SkipLocalsInitAttribute .In those cases, you can add the SkipLocalsInitAttribute. Você pode adicioná-lo a um único método ou propriedade, ou a um class ,, struct interface ou até mesmo a um módulo.You may add it to a single method or property, or to a class, struct, interface, or even a module. Esse atributo não afeta abstract os métodos; ele afeta o código gerado para a implementação.This attribute doesn't affect abstract methods; it affects the code generated for the implementation.

Esses recursos podem melhorar o desempenho em alguns cenários.These features can improve performance in some scenarios. Eles devem ser usados somente após um parâmetro de comparação cuidadoso antes e depois da adoção.They should be used only after careful benchmarking both before and after adoption. O código que envolve inteiros de tamanho nativo deve ser testado em várias plataformas de destino com tamanhos de inteiro diferentes.Code involving native sized integers must be tested on multiple target platforms with different integer sizes. Os outros recursos exigem código não seguro.The other features require unsafe code.

Recursos de ajuste e términoFit and finish features

Muitos dos outros recursos ajudam a escrever código com mais eficiência.Many of the other features help you write code more efficiently. No C# 9,0, você pode omitir o tipo em uma new expressão quando o tipo do objeto criado já é conhecido.In C# 9.0, you can omit the type in a new expression when the created object's type is already known. O uso mais comum está em declarações de campo:The most common use is in field declarations:

private List<WeatherObservation> _observations = new();

O tipo de destino new também pode ser usado quando você precisa criar um novo objeto para passar como um argumento para um método.Target-typed new can also be used when you need to create a new object to pass as an argument to a method. Considere um ForecastFor() método com a seguinte assinatura:Consider a ForecastFor() method with the following signature:

public WeatherForecast ForecastFor(DateTime forecastDate, WeatherForecastOptions options)

Você pode chamá-lo da seguinte maneira:You could call it as follows:

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

Outro bom uso para esse recurso é combiná-lo com propriedades init somente para inicializar um novo objeto: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" };

Você pode retornar uma instância criada pelo construtor padrão usando uma return new(); instrução.You can return an instance created by the default constructor using a return new(); statement.

Um recurso semelhante melhora a resolução de tipo de destino de expressões condicionais.A similar feature improves the target type resolution of conditional expressions. Com essa alteração, as duas expressões não precisam ter uma conversão implícita de uma para a outra, mas podem ter conversões implícitas em um tipo de destino.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. Você provavelmente não perceberá essa alteração.You likely won’t notice this change. O que você observará é que algumas expressões condicionais que antes exigiam conversões ou que não compilaram agora só funcionam.What you will notice is that some conditional expressions that previously required casts or wouldn’t compile now just work.

A partir do C# 9,0, você pode adicionar o static modificador a expressões lambda ou a métodos anônimos.Starting in C# 9.0, you can add the static modifier to lambda expressions or anonymous methods. As expressões lambda estáticas são análogas às static funções locais: um método lambda ou anônimo estático não pode capturar variáveis locais ou estado de instância.Static lambda expressions are analogous to the static local functions: a static lambda or anonymous method can't capture local variables or instance state. O static modificador impede a captura acidental de outras variáveis.The static modifier prevents accidentally capturing other variables.

Os tipos de retorno covariantes fornecem flexibilidade para os tipos de retorno dos métodos de substituição .Covariant return types provide flexibility for the return types of override methods. Um método override pode retornar um tipo derivado do tipo de retorno do método base substituído.An override method can return a type derived from the return type of the overridden base method. Isso pode ser útil para registros e outros tipos que dão suporte a métodos de clonagem ou de alocador virtual.This can be useful for records and for other types that support virtual clone or factory methods.

Além disso, o foreach loop reconhecerá e usará um método de extensão GetEnumerator que, de outra forma, atende ao foreach padrão.In addition, the foreach loop will recognize and use an extension method GetEnumerator that otherwise satisfies the foreach pattern. Essa alteração significa foreach ser consistente com outras construções baseadas em padrão, como o padrão assíncrono e a desconstrução baseada em padrões.This change means foreach is consistent with other pattern-based constructions such as the async pattern, and pattern-based deconstruction. Na prática, essa alteração significa que você pode adicionar foreach suporte a qualquer tipo.In practice, this change means you can add foreach support to any type. Você deve limitar seu uso ao ao enumerar um objeto faz sentido em seu design.You should limit its use to when enumerating an object makes sense in your design.

Em seguida, você pode usar os descartes como parâmetros para expressões lambda.Next, you can use discards as parameters to lambda expressions. Essa conveniência permite que você evite nomear o argumento, e o compilador pode evitar usá-lo.This convenience enables you to avoid naming the argument, and the compiler may avoid using it. Você usa o _ para qualquer argumento.You use the _ for any argument. Para obter mais informações, consulte a seção parâmetros de entrada de uma expressão lambda do artigo expressões lambda .For more information, see the Input parameters of a lambda expression section of the Lambda expressions article.

Por fim, agora você pode aplicar atributos a funções locais.Finally, you can now apply attributes to local functions. Por exemplo, você pode aplicar anotações de atributo anulável a funções locais.For example, you can apply nullable attribute annotations to local functions.

Suporte para geradores de códigoSupport for code generators

Dois recursos finais dão suporte a geradores de código C#.Two final features support C# code generators. Os geradores de código C# são um componente que você pode escrever que é semelhante a um analisador de Roslyn ou a uma correção de código.C# code generators are a component you can write that is similar to a roslyn analyzer or code fix. A diferença é que os geradores de código analisam o código e gravam novos arquivos de código-fonte como parte do processo de compilação.The difference is that code generators analyze code and write new source code files as part of the compilation process. Um gerador de código típico pesquisa código em busca de atributos ou outras convenções.A typical code generator searches code for attributes or other conventions.

Um gerador de código lê atributos ou outros elementos de código usando as APIs de análise de Roslyn.A code generator reads attributes or other code elements using the Roslyn analysis APIs. A partir dessas informações, ele adiciona um novo código à compilação.From that information, it adds new code to the compilation. Os geradores de origem só podem adicionar código; Eles não têm permissão para modificar nenhum código existente na compilação.Source generators can only add code; they aren't allowed to modify any existing code in the compilation.

Os dois recursos adicionados para geradores de código são extensões para sintaxe do método parcial _ e _inicializadores de módulo*.The two features added for code generators are extensions to partial method syntax _, and _module initializers*. Primeiro, as alterações em métodos parciais.First, the changes to partial methods. Antes do C# 9,0, os métodos parciais são private , mas não podem especificar um modificador de acesso, ter um void retorno e não podem ter out parâmetros.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. Essas restrições destinam-se que, se nenhuma implementação de método for fornecida, o compilador removerá todas as chamadas para o método parcial.These restrictions meant that if no method implementation is provided, the compiler removes all calls to the partial method. O C# 9,0 remove essas restrições, mas requer que as declarações de método parciais tenham uma implementação.C# 9.0 removes these restrictions, but requires that partial method declarations have an implementation. Os geradores de código podem fornecer essa implementação.Code generators can provide that implementation. Para evitar a introdução de uma alteração significativa, o compilador considera qualquer método parcial sem um modificador de acesso para seguir as regras antigas.To avoid introducing a breaking change, the compiler considers any partial method without an access modifier to follow the old rules. Se o método parcial incluir o private modificador de acesso, as novas regras regem esse método parcial.If the partial method includes the private access modifier, the new rules govern that partial method.

O segundo novo recurso para geradores de código é _ inicializadores de módulo *.The second new feature for code generators is _*module initializers**. Inicializadores de módulo são métodos que têm o ModuleInitializerAttribute atributo anexado a eles.Module initializers are methods that have the ModuleInitializerAttribute attribute attached to them. Esses métodos serão chamados pelo tempo de execução antes de qualquer outro acesso de campo ou invocação de método dentro do módulo inteiro.These methods will be called by the runtime before any other field access or method invocation within the entire module. Um método inicializador de módulo:A module initializer method:

  • Deve ser estáticoMust be static
  • Não deve ter parâmetrosMust be parameterless
  • Deve retornar voidMust return void
  • Não deve ser um método genéricoMust not be a generic method
  • Não deve estar contido em uma classe genéricaMust not be contained in a generic class
  • Deve ser acessível do módulo que a contémMust be accessible from the containing module

Esse último ponto de marcador efetivamente significa que o método e sua classe recipiente devem ser internos ou públicos.That last bullet point effectively means the method and its containing class must be internal or public. O método não pode ser uma função local.The method can't be a local function.