O que há de novo no C# 7,0 até C# 7,3What's new in C# 7.0 through C# 7.3

O c# 7,0 até o C# 7,3 trouxe vários recursos e aprimoramentos incrementais em sua experiência de desenvolvimento com o C#.C# 7.0 through C# 7.3 brought a number of features and incremental improvements to your development experience with C#. Este artigo fornece uma visão geral dos novos recursos de linguagem e opções de compilador.This article provides an overview of the new language features and compiler options. As descrições descrevem o comportamento do C# 7,3, que é a versão mais recente com suporte para aplicativos baseados em .NET Framework.The descriptions describe the behavior for C# 7.3, which is the most recent version supported for .NET Framework-based applications.

O elemento de configuração de seleção de versão de idioma foi adicionado com C# 7,1, que permite que você especifique a versão do idioma do compilador no arquivo do projeto.The language version selection configuration element was added with C# 7.1, which enables you to specify the compiler language version in your project file.

O c# 7.0-7.3 adiciona esses recursos e temas à linguagem C#:C# 7.0-7.3 adds these features and themes to the C# language:

  • Tuplas e DescartesTuples and discards
    • Você pode criar tipos simples e sem nome que contêm vários campos públicos.You can create lightweight, unnamed types that contain multiple public fields. Compiladores e ferramentas IDE entendem a semântica desses tipos.Compilers and IDE tools understand the semantics of these types.
    • Descartes são variáveis temporárias de somente gravação usadas em atribuições quando o valor atribuído não tem importância.Discards are temporary, write-only variables used in assignments when you don't care about the value assigned. Eles são mais úteis ao desconstruir tuplas e tipos definidos pelo usuário, bem como ao chamar métodos com parâmetros out.They're most useful when deconstructing tuples and user-defined types, as well as when calling methods with out parameters.
  • Correspondência de padrõesPattern Matching
    • Você pode criar a lógica de ramificação com base em tipos e valores arbitrários dos membros desses tipos.You can create branching logic based on arbitrary types and values of the members of those types.
  • async``Mainmétodoasync Main method
    • O ponto de entrada para um aplicativo pode ter o modificador async.The entry point for an application can have the async modifier.
  • Funções locaisLocal Functions
    • Você pode aninhar funções dentro de outras funções para limitar seu escopo e visibilidade.You can nest functions inside other functions to limit their scope and visibility.
  • Mais membros aptos para expressãoMore expression-bodied members
    • A lista de membros que podem ser criados usando expressões cresceu.The list of members that can be authored using expressions has grown.
  • throw Expressõesthrow Expressions
    • Gere exceções em constructos de código que anteriormente não eram permitidos devido ao fato de throw ser uma instrução.You can throw exceptions in code constructs that previously weren't allowed because throw was a statement.
  • default expressões literaisdefault literal expressions
    • Use expressões literais padrão em expressões de valor padrão quando o tipo de destino pode ser inferido.You can use default literal expressions in default value expressions when the target type can be inferred.
  • Aprimoramentos da sintaxe de literais numéricosNumeric literal syntax improvements
    • Novos tokens aprimoram a legibilidade para constantes numéricas.New tokens improve readability for numeric constants.
  • out asout variables
    • Declare valores out embutidos como argumentos para o método em que eles são usados.You can declare out values inline as arguments to the method where they're used.
  • Argumentos nomeados que não estejam à direitaNon-trailing named arguments
    • Os argumentos nomeados podem ser seguidos por argumentos posicionais.Named arguments can be followed by positional arguments.
  • private protected modificador de acessoprivate protected access modifier
    • O modificador de acesso private protected permite o acesso a classes derivadas no mesmo assembly.The private protected access modifier enables access for derived classes in the same assembly.
  • Resolução de sobrecarga aprimoradaImproved overload resolution
    • Novas regras para resolver ambiguidade na resolução de sobrecarga.New rules to resolve overload resolution ambiguity.
  • Técnicas para escrever código eficiente seguroTechniques for writing safe efficient code
    • Uma combinação de aprimoramentos de sintaxe que permitem trabalhar com tipos de valor usando a semântica de referência.A combination of syntax improvements that enable working with value types using reference semantics.

Por fim, o compilador tem novas opções:Finally, the compiler has new options:

  • -refout e -refonly essa geração de assembly de referênciade controle.-refout and -refonly that control reference assembly generation.
  • -publicsign para habilitar a assinatura de Software de código aberto (OSS) de assemblies.-publicsign to enable Open Source Software (OSS) signing of assemblies.
  • -pathmap para fornecer um mapeamento para diretórios de origem.-pathmap to provide a mapping for source directories.

O restante deste artigo fornece uma visão geral de cada recurso.The remainder of this article provides an overview of each feature. Para cada recurso, você aprenderá o raciocínio por trás dele e a sintaxe.For each feature, you'll learn the reasoning behind it and the syntax. Você pode explorar esses recursos em seu ambiente usando a ferramenta global dotnet try:You can explore these features in your environment using the dotnet try global tool:

  1. Instale a ferramenta global dotnet-try.Install the dotnet-try global tool.
  2. Clone o repositório dotnet/try-samples.Clone the dotnet/try-samples repository.
  3. Definir o diretório atual do subdiretório csharp7 para o repositório try-samples.Set the current directory to the csharp7 subdirectory for the try-samples repository.
  4. Execute dotnet try.Run dotnet try.

Tuplas e DescartesTuples and discards

O C# fornece uma sintaxe avançada para classes e structs que são usados para explicar a intenção do design.C# provides a rich syntax for classes and structs that is used to explain your design intent. Mas, às vezes, essa sintaxe avançada requer trabalho adicional com poucas vantagens.But sometimes that rich syntax requires extra work with minimal benefit. Geralmente, você pode escrever métodos que precisam de uma estrutura simples que contém mais de um elemento de dados.You may often write methods that need a simple structure containing more than one data element. Para dar suporte a esses cenários foram adicionadas tuplas ao C#.To support these scenarios tuples were added to C#. As tuplas são estruturas de dados leves que contêm vários campos para representar os membros de dados.Tuples are lightweight data structures that contain multiple fields to represent the data members. Os campos não são validados e você não pode definir seus próprios métodos.The fields aren't validated, and you can't define your own methods. Os tipos de tupla C# dão suporte a == e != .C# tuple types support == and !=. Para obter mais informações.For more information.

Observação

As tuplas estavam disponíveis antes do C# 7.0, mas elas eram ineficientes e não tinham nenhum suporte de linguagem.Tuples were available before C# 7.0, but they were inefficient and had no language support. Isso significava que os elementos de tupla só podiam ser referenciados como Item1, Item2 e assim por diante.This meant that tuple elements could only be referenced as Item1, Item2 and so on. O C# 7.0 introduz o suporte de linguagem para tuplas, que permite nomes semânticos para os campos de uma tupla, usando tipos de tupla novos e mais eficientes.C# 7.0 introduces language support for tuples, which enables semantic names for the fields of a tuple using new, more efficient tuple types.

Você pode criar uma tupla atribuindo um valor a cada membro e, opcionalmente, fornecendo nomes semânticos para cada um dos membros da tupla:You can create a tuple by assigning a value to each member, and optionally providing semantic names to each of the members of the tuple:

(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

A tupla namedLetters contém campos denominados Alpha e Beta.The namedLetters tuple contains fields referred to as Alpha and Beta. Esses nomes existem somente no momento da compilação e não são preservados, por exemplo, ao inspecionar a tupla usando reflexão em tempo de execução.Those names exist only at compile time and aren't preserved, for example when inspecting the tuple using reflection at run time.

Em uma atribuição de tupla, você também pode especificar os nomes dos campos no lado direito da atribuição:In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment:

var alphabetStart = (Alpha: "a", Beta: "b");
Console.WriteLine($"{alphabetStart.Alpha}, {alphabetStart.Beta}");

Pode haver ocasiões em que você deseja descompactar os membros de uma tupla que foram retornados de um método.There may be times when you want to unpackage the members of a tuple that were returned from a method. Você pode fazer isso declarando variáveis separadas para cada um dos valores na tupla.You can do that by declaring separate variables for each of the values in the tuple. Essa descompactação é chamada desconstrução da tupla:This unpackaging is called deconstructing the tuple:

(int max, int min) = Range(numbers);
Console.WriteLine(max);
Console.WriteLine(min);

Você também pode fornecer uma desconstrução semelhante para qualquer tipo no .NET.You can also provide a similar deconstruction for any type in .NET. Escreva um método Deconstruct como um membro da classe.You write a Deconstruct method as a member of the class. esse método Deconstruct fornece um conjunto de argumentos out para cada uma das propriedades que você deseja extrair.That Deconstruct method provides a set of out arguments for each of the properties you want to extract. Considere essa classe Point que fornece um método desconstrutor que extrai as coordenadas X e Y:Consider this Point class that provides a deconstructor method that extracts the X and Y coordinates:

public class Point
{
    public Point(double x, double y)
        => (X, Y) = (x, y);

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

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

Extraia os campos individuais atribuindo um Point a uma tupla: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;

Muitas vezes, quando você Inicializa uma tupla, as variáveis usadas para o lado direito da atribuição são as mesmas que os nomes desejados para os elementos de tupla: os nomes dos elementos de tupla podem ser inferidos das variáveis usadas para inicializar a tupla:Many times when you initialize a tuple, the variables used for the right side of the assignment are the same as the names you'd like for the tuple elements: The names of tuple elements can be inferred from the variables used to initialize the tuple:

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

Você pode saber mais sobre esse recurso no artigo tipos de tupla .You can learn more about this feature in the Tuple types article.

Geralmente, ao desconstruir uma tupla ou chamar um método com parâmetros out, você é forçado a definir uma variável cujo valor não é importante e você não pretende usar.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. O C# adiciona suporte para descartes para lidar com esse cenário.C# adds support for discards to handle this scenario. Um descarte é uma variável de somente gravação cujo nome é _ (o caractere de sublinhado); você pode atribuir todos os valores que você pretende descartar à variável única.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. Um descarte é como uma variável não atribuída. Além da instrução de atribuição, o descarte não pode ser usado no código.A discard is like an unassigned variable; apart from the assignment statement, the discard can't be used in code.

Os descartes são compatíveis com os seguintes cenários:Discards are supported in the following scenarios:

  • Ao desconstruir tuplas ou tipos definidos pelo usuário.When deconstructing tuples or user-defined types.
  • Ao chamar métodos com parâmetros out.When calling methods with out parameters.
  • Em uma operação de correspondência de padrões com as instruções is e switch.In a pattern matching operation with the is and switch statements.
  • Como um identificador autônomo quando você deseja identificar explicitamente o valor de uma atribuição como um descarte.As a standalone identifier when you want to explicitly identify the value of an assignment as a discard.

O exemplo a seguir define um método QueryCityDataForYears que retorna uma tupla de 6 que contém dados de dois anos diferentes para uma cidade.The following example defines a QueryCityDataForYears method that returns a 6-tuple that contains data for a city for two different years. A chamada do método no exemplo é relacionada somente com os dois valores de população retornados pelo método e, por isso, trata os valores restantes na tupla como descartes ao desconstruir a tupla.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

Para obter mais informações, consulte Descartes.For more information, see Discards.

Correspondência de padrõesPattern matching

Correspondência de padrões é um conjunto de recursos que permitem novas maneiras de expressar o fluxo de controle em seu código.Pattern matching is a set of features that enable new ways to express control flow in your code. Você pode testar variáveis para seus tipos, valores ou os valores de suas propriedades.You can test variables for their type, values or the values of their properties. Essas técnicas criam um fluxo de código mais legível.These techniques create more readable code flow.

A correspondência de padrões tem suporte a expressões is e switch.Pattern matching supports is expressions and switch expressions. Cada uma delas permite inspecionar um objeto e suas propriedades para determinar se esse objeto satisfaz o padrão procurado.Each enables inspecting an object and its properties to determine if that object satisfies the sought pattern. Você usa a palavra-chave when para especificar regras adicionais para o padrão.You use the when keyword to specify additional rules to the pattern.

A is expressão de padrão estende o is operador familiar para consultar um objeto sobre seu tipo e atribuir o resultado em uma instrução.The is pattern expression extends the familiar is operator to query an object about its type and assign the result in one instruction. O seguinte código verifica se uma variável é um int e, nesse caso, adiciona-a à soma atual:The following code checks if a variable is an int, and if so, adds it to the current sum:

if (input is int count)
    sum += count;

O pequeno exemplo anterior demonstra as melhorias na expressão is.The preceding small example demonstrates the enhancements to the is expression. Você pode realizar o teste nos tipos de valor, bem como nos tipos de referência, e atribuir o resultado com êxito a uma nova variável do tipo correto.You can test against value types as well as reference types, and you can assign the successful result to a new variable of the correct type.

A expressão de correspondência switch tem uma sintaxe conhecida, baseada na instrução switch que já faz parte da linguagem C#.The switch match expression has a familiar syntax, based on the switch statement already part of the C# language. A instrução switch atualizada tem vários novos constructos:The updated switch statement has several new constructs:

  • O tipo controlador de uma expressão switch não está mais restrito a tipos integrais, tipos Enum, string ou a um tipo que permite valor nulo correspondente a um desses tipos.The governing type of a switch expression is no longer restricted to integral types, Enum types, string, or a nullable type corresponding to one of those types. Qualquer tipo pode ser usado.Any type may be used.
  • Teste o tipo da expressão switch em cada rótulo case.You can test the type of the switch expression in each case label. Assim como ocorre com a expressão is, você pode atribuir uma nova variável a esse tipo.As with the is expression, you may assign a new variable to that type.
  • Adicione uma cláusula when para testar mais condições de teste nessa variável.You may add a when clause to further test conditions on that variable.
  • A ordem dos rótulos case agora é importante.The order of case labels is now important. O primeiro branch para correspondência é executado; os outros são ignorados.The first branch to match is executed; others are skipped.

O seguinte código demonstra esses novos recursos:The following code demonstrates these new features:

public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
        switch (i)
        {
            case 0:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
        }
    }
    return sum;
}
  • case 0: é um padrão constante.case 0: is a constant pattern.
  • case IEnumerable<int> childSequence: é um padrão de declaração.case IEnumerable<int> childSequence: is a declaration pattern.
  • case int n when n > 0: é um padrão de declaração com uma when condição adicional.case int n when n > 0: is a declaration pattern with an additional when condition.
  • case null: é o null padrão constante.case null: is the null constant pattern.
  • default: é o caso padrão conhecido.default: is the familiar default case.

A partir do C# 7.1, a expressão de padrão para o padrão de tipo is e switch pode ter o tipo de um parâmetro de tipo genérico.Beginning with C# 7.1, the pattern expression for is and the switch type pattern may have the type of a generic type parameter. Isso pode ser mais útil ao verificar tipos que podem ser do tipo struct ou class e você deseja evitar conversão boxing.This can be most useful when checking types that may be either struct or class types, and you want to avoid boxing.

Saiba mais sobre a correspondência de padrões em Correspondência de padrões em C#.You can learn more about pattern matching in Pattern Matching in C#.

Async mainAsync main

Um método async main permite que você use await no método Main.An async main method enables you to use await in your Main method. Anteriormente, você precisava escrever:Previously you would need to write:

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

Agora, você pode escrever:You can now write:

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

Se o programa não retorna um código de saída, declare um método Main que retorna um Task:If your program doesn't return an exit code, you can declare a Main method that returns a Task:

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

Leia mais sobre os detalhes no tópico async main no guia de programação.You can read more about the details in the async main article in the programming guide.

Funções locaisLocal functions

Muitos designs para classes incluem métodos que são chamados de apenas um local.Many designs for classes include methods that are called from only one location. Esses métodos privados adicionais mantêm cada método pequeno e focado.These additional private methods keep each method small and focused. As funções locais permitem que você declare métodos dentro do contexto de outro método.Local functions enable you to declare methods inside the context of another method. Funções locais tornam mais fácil para os leitores da classe verem que o método local é chamado apenas do contexto em que é declarado.Local functions make it easier for readers of the class to see that the local method is only called from the context in which it is declared.

Há dois casos de uso muito comuns para funções locais: métodos iteradores públicos e métodos assíncronos públicos.There are two common use cases for local functions: public iterator methods and public async methods. Ambos os tipos de métodos de geram código que relata os erros mais tarde do que os programadores podem esperar.Both types of methods generate code that reports errors later than programmers might expect. Em métodos iteradores, as exceções são observadas apenas ao chamar o código que enumera a sequência retornada.In iterator methods, any exceptions are observed only when calling code that enumerates the returned sequence. Em métodos assíncronos, as exceções são observadas apenas quando a Task retornada é aguardada.In async methods, any exceptions are only observed when the returned Task is awaited. O seguinte exemplo demonstra a validação de parâmetro de separação da implementação do iterador usando uma função local:The following example demonstrates separating parameter validation from the iterator implementation using a local function:

public static IEnumerable<char> AlphabetSubset3(char start, char end)
{
    if (start < 'a' || start > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end < 'a' || end > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

    if (end <= start)
        throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

    return alphabetSubsetImplementation();

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

A mesma técnica pode ser empregada com métodos async para garantir que as exceções decorrentes da validação de argumento sejam lançadas antes de o trabalho assíncrono começar: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.";
    }
}

Agora há suporte para esta sintaxe:This syntax is now supported:

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

O atributo SomeThingAboutFieldAttribute é aplicado ao campo de suporte gerado pelo compilador para SomeProperty.The attribute SomeThingAboutFieldAttribute is applied to the compiler generated backing field for SomeProperty. Para saber mais, confira atributos no guia de programação em C#.For more information, see attributes in the C# programming guide.

Observação

Alguns dos designs com suporte das funções locais também podem ser realizados usando expressões lambda.Some of the designs that are supported by local functions can also be accomplished using lambda expressions. Para obter mais informações, consulte funções locais versus expressões lambda.For more information, see Local functions vs. lambda expressions.

Mais membros aptos para expressãoMore expression-bodied members

O C# 6 introduziu Membros Expression-apto para para funções membro e propriedades somente leitura.C# 6 introduced expression-bodied members for member functions and read-only properties. O C# 7.0 expande os membros permitidos que podem ser implementados como expressões.C# 7.0 expands the allowed members that can be implemented as expressions. No C# 7.0, você pode implementar construtores, finalizadores e acessadores get e set em propriedades e indexadores.In C# 7.0, you can implement constructors, finalizers, and get and set accessors on properties and indexers. O código a seguir mostra exemplos de cada um: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";
}

Observação

Este exemplo não precisa de um finalizador, mas ele é mostrado para demonstrar a sintaxe.This example does not need a finalizer, but it is shown to demonstrate the syntax. Você não deve implementar um finalizador em sua classe a menos que seja necessário para liberar recursos não gerenciados.You should not implement a finalizer in your class unless it is necessary to release unmanaged resources. Você também deve considerar o uso da classe SafeHandle em vez de gerenciar recursos não gerenciados diretamente.You should also consider using the SafeHandle class instead of managing unmanaged resources directly.

Esses novos locais para membros aptos para expressão representam uma etapa importante para a linguagem C#: esses recursos foram implementados por membros da comunidade trabalhando no projeto Roslyn de software livre.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.

A alteração de um método para um membro de corpo da expressão é uma alteração compatível com binário.Changing a method to an expression bodied member is a binary compatible change.

Expressões throwThrow expressions

No C#, throw sempre foi uma instrução.In C#, throw has always been a statement. Como throw é uma instrução, não uma expressão, havia constructos do C# em que não era possível usá-la.Because throw is a statement, not an expression, there were C# constructs where you couldn't use it. Eles incluíam expressões condicionais, expressões de união nulas e algumas expressões lambda.These included conditional expressions, null coalescing expressions, and some lambda expressions. A adição de membros aptos para expressão inclui mais locais em que as expressões throw seriam úteis.The addition of expression-bodied members adds more locations where throw expressions would be useful. Para que você possa escrever qualquer um desses construtos, o C# 7.0 apresenta expressões throw.So that you can write any of these constructs, C# 7.0 introduces throw expressions.

Essa adição facilita a escrita de um código mais baseado em expressão.This addition makes it easier to write more expression-based code. Você não precisa de instruções adicionais para a verificação de erros.You don't need additional statements for error checking.

Expressões literais padrãoDefault literal expressions

Expressões literais padrão são uma melhoria das expressões de valor padrão.Default literal expressions are an enhancement to default value expressions. Essas expressões inicializam uma variável com o valor padrão.These expressions initialize a variable to the default value. Nos casos em que você anteriormente escrevia:Where you previously would write:

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

Agora você pode omitir o tipo no lado direito da inicialização:You can now omit the type on the right-hand side of the initialization:

Func<string, bool> whereClause = default;

Para saber mais, confira a seção Literais padrão do artigo do operador padrão.For more information, see the default literal section of the default operator article.

Aprimoramentos da sintaxe de literais numéricosNumeric literal syntax improvements

A leitura incorreta das constantes numéricas pode fazer com que seja difícil entender o código ao lê-lo pela primeira vez.Misreading numeric constants can make it harder to understand code when reading it for the first time. Bitmasks ou outros valores simbólicos são propensos a equívocos.Bit masks or other symbolic values are prone to misunderstanding. O C# 7.0 inclui dois novos recursos para a escrita de números da maneira mais legível possível para o uso pretendido: literais binários e separadores de dígito.C# 7.0 includes two new features to write numbers in the most readable fashion for the intended use: binary literals, and digit separators.

Para ocasiões em que você estiver criando bitmasks ou sempre que uma representação binária de um número tornar o código mais legível, escreva esse número em binário:For those times when you're creating bit masks, or whenever a binary representation of a number makes the most readable code, write that number in binary:

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

O 0b no início da constante indica que o número é escrito como um número binário.The 0b at the beginning of the constant indicates that the number is written as a binary number. Os números binários podem chegar longos e, em geral, é mais fácil ver os padrões de bit introduzindo o _ como um separador de dígito, conforme mostrado na constante binária no exemplo anterior.Binary numbers can get long, so it's often easier to see the bit patterns by introducing the _ as a digit separator, as shown in the binary constant in the preceding example. O separador de dígitos pode aparecer em qualquer lugar na constante.The digit separator can appear anywhere in the constant. Para números de base 10, é comum usá-lo como separador de milhar.For base 10 numbers, it is common to use it as a thousands separator. Os literais numéricos hexadecimais e binários podem começar com um _ :Hex and binary numeric literals may begin with an _:

public const long BillionsAndBillions = 100_000_000_000;

O separador de dígito pode ser usado com os tipos decimal, float e double também: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;

Juntos, você pode declarar constantes numéricas com muito mais facilidade de leitura.Taken together, you can declare numeric constants with much more readability.

Variáveis outout variables

A sintaxe existente que dá suporte a out parâmetros foi aprimorada no C# 7.The existing syntax that supports out parameters has been improved in C# 7. Agora você pode declarar variáveis out na lista de argumentos de uma chamada de método, em vez de escrever uma instrução de declaração separada: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");

Talvez você queira especificar o tipo da out variável para fins de clareza, conforme mostrado no exemplo anterior.You may want to specify the type of the out variable for clarity, as shown in the preceding example. No entanto, a linguagem dá suporte ao uso de variável local de tipo implícito: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");
  • O código é mais fácil de ler.The code is easier to read.
    • Você declara a variável out onde você a usa, não em uma linha de código anterior.You declare the out variable where you use it, not on a preceding line of code.
  • Não é necessário atribuir um valor inicial.No need to assign an initial value.
    • Ao declarar a variável out no local em que ela é usada em uma chamada de método, você não pode usá-la acidentalmente antes de ela ser atribuída.By declaring the out variable where it's used in a method call, you can't accidentally use it before it is assigned.

A sintaxe adicionada no C# 7.0 para permitir declarações de variável out foi estendida para incluir inicializadores de campo, inicializadores de propriedade, inicializadores de construtor e cláusulas de consulta.The syntax added in C# 7.0 to allow out variable declarations has been extended to include field initializers, property initializers, constructor initializers, and query clauses. Ela permite código como no seguinte exemplo:It enables code such as the following example:

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

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

Argumentos nomeados que não estejam à direitaNon-trailing named arguments

Agora as chamadas de método podem usar argumentos nomeados que precedem argumentos posicionais quando esses argumentos nomeados estão nas posições corretas.Method calls may now use named arguments that precede positional arguments when those named arguments are in the correct positions. Para obter mais informações, consulte argumentos nomeados e opcionais.For more information, see Named and optional arguments.

modificador de acesso protegido privadoprivate protected access modifier

Um novo modificador de acesso composto: private protected indica que um membro pode ser acessado pela classe que o contém ou por classes derivadas que são declaradas no mesmo assembly.A new compound access modifier: private protected indicates that a member may be accessed by containing class or derived classes that are declared in the same assembly. Enquanto que protected internal permite o acesso por classes derivadas ou classes que estejam no mesmo assembly, o private protected limita o acesso a tipos derivados declarados no mesmo assembly.While protected internal allows access by derived classes or classes that are in the same assembly, private protected limits access to derived types declared in the same assembly.

Para obter mais informações, consulte modificadores de acesso na referência de linguagem.For more information, see access modifiers in the language reference.

Candidatos de sobrecarga aprimoradosImproved overload candidates

Em cada versão, as regras de resolução de sobrecarga são atualizadas para resolver situações em que as chamadas de método ambíguas têm uma opção "óbvia".In every release, the overload resolution rules get updated to address situations where ambiguous method invocations have an "obvious" choice. Esta versão adiciona três novas regras para ajudar o compilador a escolher a opção óbvia:This release adds three new rules to help the compiler pick the obvious choice:

  1. Quando um grupo de métodos contém membros de instância e estáticos, o compilador descartará os membros da instância se o método tiver sido chamado sem um receptor ou contexto de instância.When a method group contains both instance and static members, the compiler discards the instance members if the method was invoked without an instance receiver or context. O compilador descartará os membros estáticos se o método tiver sido chamado com um receptor de instância.The compiler discards the static members if the method was invoked with an instance receiver. Quando não há receptor, o compilador inclui apenas membros estáticos em um contexto estático, caso contrário, membros estáticos e de instância.When there is no receiver, the compiler includes only static members in a static context, otherwise both static and instance members. Quando o receptor é ambiguamente uma instância ou um tipo, o compilador inclui ambos.When the receiver is ambiguously an instance or type, the compiler includes both. Um contexto estático, em que não é possível usar um receptor de instância this implícito, inclui o corpo de membros em que nenhum this é definido, como membros estáticos, bem como locais onde this não pode ser usado, como inicializadores de campo e inicializadores de construtor.A static context, where an implicit this instance receiver cannot be used, includes the body of members where no this is defined, such as static members, as well as places where this cannot be used, such as field initializers and constructor-initializers.
  2. Quando um grupo de métodos contém alguns métodos genéricos cujos argumentos de tipo não satisfazem suas restrições, esses membros são removidos do conjunto de candidatos.When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set.
  3. Para uma conversão de grupo de métodos, os métodos candidatos cujo tipo de retorno não corresponda ao tipo de retorno do delegado são removidos do conjunto.For a method group conversion, candidate methods whose return type doesn't match up with the delegate's return type are removed from the set.

Você notará essa mudança somente porque encontrará menos erros de compilador para sobrecargas de métodos ambíguos quando tiver certeza de qual método é melhor.You'll only notice this change because you'll find fewer compiler errors for ambiguous method overloads when you are sure which method is better.

Como habilitar código seguro mais eficienteEnabling more efficient safe code

Você deve ser capaz de escrever um código C# de forma segurança que seja executado tão bem quanto o código não seguro.You should be able to write C# code safely that performs as well as unsafe code. O código seguro evita classes de erros, como estouros de buffer, ponteiros perdidos e outros erros de acesso à memória.Safe code avoids classes of errors, such as buffer overruns, stray pointers, and other memory access errors. Esses novos recursos expandem as capacidades do código seguro verificável.These new features expand the capabilities of verifiable safe code. Tente escrever mais partes do seu código usando construções seguras.Strive to write more of your code using safe constructs. Esses recursos tornam isso mais fácil.These features make that easier.

Os novos recursos a seguir são compatíveis com o tema de melhor desempenho para código seguro:The following new features support the theme of better performance for safe code:

  • Você pode acessar campos fixos sem fixação.You can access fixed fields without pinning.
  • Você pode reatribuir variáveis locais ref.You can reassign ref local variables.
  • Você pode usar inicializadores em matrizes stackalloc.You can use initializers on stackalloc arrays.
  • Você pode usar instruções fixed com qualquer tipo compatível com um padrão.You can use fixed statements with any type that supports a pattern.
  • Você pode usar restrições genéricas adicionais.You can use additional generic constraints.
  • O modificador in em parâmetros, para especificar que um argumento é passado por referência, mas não modificado pelo método chamado.The in modifier on parameters, to specify that an argument is passed by reference but not modified by the called method. Adicionar o modificador in a um argumento é uma alteração compatível com a origem.Adding the in modifier to an argument is a source compatible change.
  • O modificador ref readonly nos retornos de método, para indicar que um método retorna seu valor por referência, mas não permite gravações nesse objeto.The ref readonly modifier on method returns, to indicate that a method returns its value by reference but doesn't allow writes to that object. Adicionar o modificador ref readonly é uma alteração compatível com a origem se o retorno é atribuído a um valor.Adding the ref readonly modifier is a source compatible change, if the return is assigned to a value. Adicionar o modificador readonly a uma instrução de retorno ref existente é uma alteração incompatível.Adding the readonly modifier to an existing ref return statement is an incompatible change. Ela requer que os chamadores atualizem a declaração de ref variáveis locais para incluir o modificador readonly.It requires callers to update the declaration of ref local variables to include the readonly modifier.
  • A declaração readonly struct, para indicar que uma struct é imutável e deve ser passado como um parâmetro in para seus métodos de membro.The readonly struct declaration, to indicate that a struct is immutable and should be passed as an in parameter to its member methods. Adicionar o modificador readonly a uma declaração struct existente é uma alteração compatível com binário.Adding the readonly modifier to an existing struct declaration is a binary compatible change.
  • A declaração ref struct, para indicar que um tipo de struct acessa a memória gerenciada diretamente e deve sempre ser alocado por pilha.The ref struct declaration, to indicate that a struct type accesses managed memory directly and must always be stack allocated. Adicionar o modificador ref a uma declaração struct existente é uma alteração incompatível.Adding the ref modifier to an existing struct declaration is an incompatible change. Um ref struct não pode ser um membro de uma classe ou usado em outros locais em que ele pode ser alocado no heap.A ref struct cannot be a member of a class or used in other locations where it may be allocated on the heap.

Você pode ler mais sobre todas essas alterações em Escrever código eficiente seguro.You can read more about all these changes in Write safe efficient code.

Locais e retornos de refRef locals and returns

Esse recurso permite que os algoritmos que usam e retornam referências para as variáveis definidas em outro lugar.This feature enables algorithms that use and return references to variables defined elsewhere. Um exemplo é trabalhar com matrizes grandes e localizar um único local com determinadas características.One example is working with large matrices, and finding a single location with certain characteristics. O seguinte método retorna uma referência a esse armazenamento na matriz:The following method returns a reference to that storage in the matrix:

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

Você pode declarar o valor retornado como uma ref e modificar esse valor na matriz, conforme mostrado no seguinte código:You can declare the return value as a ref and modify that value in the matrix, as shown in the following code:

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

A linguagem C# tem várias regras que protegem contra o uso indevido de locais e retornos de ref:The C# language has several rules that protect you from misusing the ref locals and returns:

  • É necessário adicionar a palavra-chave ref à assinatura do método e a todas as instruções return em um método.You must add the ref keyword to the method signature and to all return statements in a method.
    • Isso torna claro que o método é retornado por referência em todo o método.That makes it clear the method returns by reference throughout the method.
  • Um ref return pode ser atribuído a uma variável de valor ou a uma variável ref.A ref return may be assigned to a value variable, or a ref variable.
    • O chamador controla se o valor retornado é copiado ou não.The caller controls whether the return value is copied or not. A omissão do modificador ref ao atribuir o valor retornado indica que o chamador deseja obter uma cópia do valor, não uma referência ao armazenamento.Omitting the ref modifier when assigning the return value indicates that the caller wants a copy of the value, not a reference to the storage.
  • Não é possível atribuir um valor retornado do método padrão a uma variável local de ref.You can't assign a standard method return value to a ref local variable.
    • Isso proíbe que instruções como ref int i = sequence.Count();That disallows statements like ref int i = sequence.Count();
  • Não é possível retornar um ref para uma variável cujo tempo de vida não se estende para além da execução do método.You can't return a ref to a variable whose lifetime doesn't extend beyond the execution of the method.
    • Isso significa que não é possível retornar uma referência a uma variável local ou a uma variável com um escopo semelhante.That means you can't return a reference to a local variable or a variable with a similar scope.
  • O locais e retornos de ref não podem ser usados com métodos assíncronos.ref locals and returns can't be used with async methods.
    • O compilador não consegue saber se a variável referenciada foi definida com o valor final quando o método assíncrono retorna.The compiler can't know if the referenced variable has been set to its final value when the async method returns.

A adição de locais e retornos de ref permite algoritmos que são mais eficientes evitando a cópia de valores ou a execução múltipla de operações de desreferenciamento.The addition of ref locals and ref returns enables algorithms that are more efficient by avoiding copying values, or performing dereferencing operations multiple times.

Adicionar ref ao valor retornado é uma alteração compatível com a origem.Adding ref to the return value is a source compatible change. O código existente é compilado, mas o valor retornado ref é copiado quando atribuído.Existing code compiles, but the ref return value is copied when assigned. Os chamadores devem atualizar o armazenamento para o valor retornado para uma variável local ref para armazenar o retorno como uma referência.Callers must update the storage for the return value to a ref local variable to store the return as a reference.

Agora, as variáveis locais ref podem ser reatribuídas para se referir a diferentes instâncias depois de serem inicializadas.Now, ref locals may be reassigned to refer to different instances after being initialized. O comando a seguir agora compila:The following code now compiles:

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

Para obter mais informações, consulte o artigo sobre ref retornos e ref locaise o artigo sobre foreach .For more information, see the article on ref returns and ref locals, and the article on foreach.

Para saber mais, confira o artigo Palavra-chave ref.For more information, see the ref keyword article.

Expressões ref condicionaisConditional ref expressions

Por fim, a expressão condicional pode produzir um resultado ref em vez de um resultado de valor.Finally, the conditional expression may produce a ref result instead of a value result. Por exemplo, você gravaria o seguinte para recuperar uma referência ao primeiro elemento em uma de duas matrizes:For example, you would write the following to retrieve a reference to the first element in one of two arrays:

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

A variável r é uma referência ao primeiro valor em arr ou otherArr.The variable r is a reference to the first value in either arr or otherArr.

Para saber mais, confira Operador condicional (?:) na referência da linguagem.For more information, see conditional operator (?:) in the language reference.

in modificador de parâmetroin parameter modifier

A in palavra-chave complementa as palavras-chaves ref e out existentes para passar argumentos por referência.The in keyword complements the existing ref and out keywords to pass arguments by reference. A palavra-chave in especifica a passagem do argumento por referência, mas o método chamado não modifica o valor.The in keyword specifies passing the argument by reference, but the called method doesn't modify the value.

Você pode declarar sobrecargas que passam por valor ou por referência ReadOnly, conforme mostrado no código a seguir:You may declare overloads that pass by value or by readonly reference, as shown in the following code:

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

A sobrecarga por valor (primeiro no exemplo anterior) é melhor do que a versão de referência ReadOnly.The by value (first in the preceding example) overload is better than the by readonly reference version. Para chamar a versão com o argumento de referência somente leitura, inclua o modificador in ao chamar o método.To call the version with the readonly reference argument, you must include the in modifier when calling the method.

Para obter mais informações, consulte o artigo sobre o in modificador de parâmetro.For more information, see the article on the in parameter modifier.

Mais tipos são compatíveis com a instrução fixedMore types support the fixed statement

A instrução fixed era compatível com um conjunto limitado de tipos.The fixed statement supported a limited set of types. A partir do C# 7.3, qualquer tipo que contenha um método GetPinnableReference() que retorna ref T ou ref readonly T pode ser fixed.Starting with C# 7.3, any type that contains a GetPinnableReference() method that returns a ref T or ref readonly T may be fixed. A adição desse recurso significa que fixed pode ser usado com System.Span<T> e tipos relacionados.Adding this feature means that fixed can be used with System.Span<T> and related types.

Para obter mais informações, consulte o artigo da fixed instrução na referência de linguagem.For more information, see the fixed statement article in the language reference.

A indexação de campos fixed não requer fixaçãoIndexing fixed fields does not require pinning

Considere este struct:Consider this struct:

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

Em versões anteriores do C#, era preciso fixar uma variável para acessar um dos números inteiros que fazem parte de myFixedField.In earlier versions of C#, you needed to pin a variable to access one of the integers that are part of myFixedField. Agora, o código a seguir compila sem fixar a variável p dentro de uma instrução fixed separada:Now, the following code compiles without pinning the variable p inside a separate fixed statement:

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

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

A variável p acessa um elemento em myFixedField.The variable p accesses one element in myFixedField. Não é necessário declarar uma variável int* separada.You don't need to declare a separate int* variable. Você ainda precisa de um unsafe contexto.You still need an unsafe context. Em versões anteriores do C#, é necessário declarar um segundo ponteiro fixo:In earlier versions of C#, you need to declare a second fixed pointer:

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

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

Para obter mais informações, consulte o artigo na fixed instrução.For more information, see the article on the fixed statement.

Matrizes stackalloc são compatíveis com inicializadoresstackalloc arrays support initializers

Você conseguiu especificar os valores dos elementos em uma matriz ao inicializá-la:You've been able to specify the values for elements in an array when you initialize it:

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

Agora, essa mesma sintaxe pode ser aplicada a matrizes declaradas com stackalloc:Now, that same syntax can be applied to arrays that are declared with stackalloc:

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

Para obter mais informações, consulte o artigo do stackalloc operador .For more information, see the stackalloc operator article.

Restrições genéricas aprimoradasEnhanced generic constraints

Agora é possível especificar o tipo System.Enum ou System.Delegate como restrições de classe base para um parâmetro de tipo.You can now specify the type System.Enum or System.Delegate as base class constraints for a type parameter.

Você também pode usar a nova unmanaged restrição para especificar que um parâmetro de tipo deve ser um tipo não- gerenciadonão anulável.You can also use the new unmanaged constraint, to specify that a type parameter must be a non-nullable unmanaged type.

Para obter mais informações, consulte os artigos sobre where restrições genéricas e restrições em parâmetros de tipo.For more information, see the articles on where generic constraints and constraints on type parameters.

Adicionar essas restrições a tipos existentes é uma alteração incompatível.Adding these constraints to existing types is an incompatible change. Tipos genéricos fechados podem não atender mais a essas novas restrições.Closed generic types may no longer meet these new constraints.

Tipos de retorno assíncrono generalizadoGeneralized async return types

Retornar um objeto Task de métodos assíncronos pode introduzir gargalos de desempenho em determinados caminhos.Returning a Task object from async methods can introduce performance bottlenecks in certain paths. Task é um tipo de referência, portanto, usá-lo significa alocar um objeto.Task is a reference type, so using it means allocating an object. Em casos em que um método declarado com o modificador async retorna um resultado armazenado em cache ou é concluído de forma síncrona, as alocações extras podem se tornar um custo de tempo significativo em seções críticas de desempenho de código.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. Isso pode se tornar caro se essas alocações ocorrem em loops rígidos.It can become costly if those allocations occur in tight loops.

O novo recurso de linguagem significa que os tipos de retorno do método assíncrono não se limitam a Task, Task<T> e void.The new language feature means that async method return types aren't limited to Task, Task<T>, and void. O tipo retornado ainda deve satisfazer o padrão assíncrono, o que significa que um método GetAwaiter deve ser acessível.The returned type must still satisfy the async pattern, meaning a GetAwaiter method must be accessible. Como um exemplo concreto, o ValueTask tipo foi adicionado ao .net para fazer uso desse novo recurso de linguagem:As one concrete example, the ValueTask type has been added to .NET to make use of this new language feature:

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

Observação

Você precisa adicionar o pacote NuGet System.Threading.Tasks.Extensions > para usar o ValueTask<TResult> tipo.You need to add the NuGet package System.Threading.Tasks.Extensions > in order to use the ValueTask<TResult> type.

Essa melhoria é mais útil para autores de biblioteca impedirem a alocação de uma Task em um código crítico para o desempenho.This enhancement is most useful for library authors to avoid allocating a Task in performance critical code.

Novas opções do compiladorNew compiler options

Novas opções do compilador são compatíveis com novos cenários de build e DevOps para programas C#.New compiler options support new build and DevOps scenarios for C# programs.

Geração de assembly de referênciaReference assembly generation

Há duas novas opções de compilador que geram assemblies somente de referência: ProduceReferenceAssembly e ProduceOnlyReferenceAssembly.There are two new compiler options that generate reference-only assemblies: ProduceReferenceAssembly and ProduceOnlyReferenceAssembly. Os artigos vinculados explicam essas opções e os assemblies de referência mais detalhadamente.The linked articles explain these options and reference assemblies in more detail.

Assinatura pública ou de código abertoPublic or Open Source signing

A opção de compilador PublicSign instrui o compilador a assinar o assembly usando uma chave pública.The PublicSign compiler option instructs the compiler to sign the assembly using a public key. O assembly é marcado como assinado, mas a assinatura é obtida da chave pública.The assembly is marked as signed, but the signature is taken from the public key. Essa opção permite criar crie assemblies assinados a partir de projetos de código aberto usando uma chave pública.This option enables you to build signed assemblies from open-source projects using a public key.

Para obter mais informações, consulte o artigo de opção do compilador PublicSign .For more information, see the PublicSign compiler option article.

pathmappathmap

A opção de compilador PathMap instrui o compilador a substituir os caminhos de origem do ambiente de compilação por caminhos de origem mapeados.The PathMap compiler option instructs the compiler to replace source paths from the build environment with mapped source paths. A opção PathMap controla o caminho de origem gravado pelo compilador em arquivos PDB ou para o CallerFilePathAttribute .The PathMap option controls the source path written by the compiler to PDB files or for the CallerFilePathAttribute.

Para obter mais informações, consulte o artigo de opção do compilador PathMap .For more information, see the PathMap compiler option article.