Tutorial: Explorar ideias usando instruções de nível superior para criar código conforme você aprende

Neste tutorial, você aprenderá como:

  • Conheça as regras que regem o uso de instruções de nível superior.
  • Use instruções de nível superior para explorar algoritmos.
  • Refatore explorações em componentes reutilizáveis.

Pré-requisitos

Você precisará configurar seu computador para executar o .NET 6, que inclui o compilador C# 10. O compilador C# 10 está disponível a partir do Visual Studio 2022 ou do .NET 6 SDK.

Este tutorial pressupõe que você esteja familiarizado com o C# e .NET, incluindo o Visual Studio ou a CLI do .NET.

Comece a explorar

As instruções de nível superior permitem evitar a cerimônia extra necessária colocando o ponto de entrada do programa em um método estático em uma classe. O ponto de partida típico para um novo aplicativo de console se parece com o seguinte código:

using System;

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

O código anterior é o resultado da execução do comando dotnet new console e da criação de um novo aplicativo de console. Essas 11 linhas contêm apenas uma linha de código executável. Você pode simplificar esse programa com o novo recurso de instruções de nível superior. Isso permite que você remova todas, exceto duas das linhas deste programa:

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

Importante

Os modelos C# para o .NET 6 usam instruções de nível superior. Se você já tiver atualizado para o .NET 6, talvez seu aplicativo não corresponda ao código descrito neste artigo. Para obter mais informações, consulte o artigo sobre Novos modelos C# geram instruções de nível superior

O SDK do .NET 6 também adiciona um conjunto de diretivas implícitasglobal using para projetos que usam os seguintes SDKs:

  • Microsoft.NET.Sdk
  • Microsoft.NET.Sdk.Web
  • Microsoft.NET.Sdk.Worker

Essas diretivas implícitas global using incluem os namespaces mais comuns para o tipo de projeto.

Para obter mais informações, consulte o artigo sobre Diretivas de uso implícito

Esse recurso simplifica o que é necessário para começar a explorar novas ideias. Você pode usar instruções de nível superior em cenários de script ou para explorar. Assim que o básico estiver funcionando, você pode começar a refatorar o código e criar métodos, classes ou outros assemblies para componentes reutilizáveis criados. As instruções de nível superior habilitam experimentações rápidas e tutoriais iniciantes. Elas também fornecem um caminho tranquilo da experimentação a programas completos.

As instruções de nível superior são executadas na ordem em que aparecem no arquivo. As instruções de nível superior só podem ser usadas em um arquivo de origem em seu aplicativo. O compilador gera um erro se você usá-los em mais de um arquivo.

Criar um computador de resposta mágico do .NET

Neste tutorial, vamos criar um aplicativo de console que responda a uma pergunta "sim" ou "não" com uma resposta aleatória. Você criará a funcionalidade passo a passo. Você pode se concentrar em sua tarefa em vez da cerimônia necessária para a estrutura de um programa típico. Em seguida, quando estiver satisfeito com a funcionalidade, você poderá refatorar o aplicativo conforme julgar adequado.

Um bom ponto de partida é gravar a pergunta de volta para o console. Você pode começar gravando o seguinte código:

Console.WriteLine(args);

Você não declara uma variável args. Para o arquivo de origem único que contém suas instruções de nível superior, o compilador reconhece args como significando os argumentos de linha de comando. O tipo de argumento é um string[], como em todos os programas C#.

O código pode ser testado executando o comando dotnet run a seguir:

dotnet run -- Should I use top level statements in all my programs?

Os argumentos após a linha de comando -- são passados para o programa. Você pode ver o tipo da variável args, porque é isso que é impresso no console:

System.String[]

Para gravar a pergunta no console, você precisará enumerar os argumentos e separá-los com um espaço. Substitua a chamada WriteLine pelo código a seguir:

Console.WriteLine();
foreach(var s in args)
{
    Console.Write(s);
    Console.Write(' ');
}
Console.WriteLine();

Agora, ao executar o programa, ele exibirá corretamente a pergunta como uma cadeia de argumentos.

Responder com uma resposta aleatória

Após ecoar a pergunta, você pode adicionar o código para gerar a resposta aleatória. Comece adicionando uma matriz de respostas possíveis:

string[] answers =
[
    "It is certain.",       "Reply hazy, try again.",     "Don’t count on it.",
    "It is decidedly so.",  "Ask again later.",           "My reply is no.",
    "Without a doubt.",     "Better not tell you now.",   "My sources say no.",
    "Yes – definitely.",    "Cannot predict now.",        "Outlook not so good.",
    "You may rely on it.",  "Concentrate and ask again.", "Very doubtful.",
    "As I see it, yes.",
    "Most likely.",
    "Outlook good.",
    "Yes.",
    "Signs point to yes.",
];

Essa matriz tem dez respostas afirmativas, cinco não confirmadas e cinco negativas. Em seguida, adicione o seguinte código para gerar e exibir uma resposta aleatória da matriz:

var index = new Random().Next(answers.Length - 1);
Console.WriteLine(answers[index]);

Você pode executar o aplicativo novamente para ver os resultados. Você deve ver algo semelhante à seguinte saída:

dotnet run -- Should I use top level statements in all my programs?

Should I use top level statements in all my programs?
Better not tell you now.

Esse código responde às perguntas, mas vamos adicionar mais um recurso. Você gostaria que seu aplicativo de perguntas simulasse o pensamento sobre a resposta. Você pode fazer isso adicionando um pouco de animação do ASCII e pausando enquanto trabalha. Adicione o seguinte código após a linha que ecoa a pergunta:

for (int i = 0; i < 20; i++)
{
    Console.Write("| -");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("/ \\");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("- |");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("\\ /");
    await Task.Delay(50);
    Console.Write("\b\b\b");
}
Console.WriteLine();

Também será necessário adicionar uma instrução using à parte superior do arquivo de origem:

using System.Threading.Tasks;

As instruções using devem aparecer antes de qualquer outra instrução no arquivo. Caso contrário, é um erro do compilador. Você pode executar o programa novamente e ver a animação. Isso torna uma experiência melhor. Escolha um comprimento do atraso que corresponda ao seu gosto.

O código anterior cria um conjunto de linhas de rotação separadas por um espaço. A adição da palavra-chave await instrui o compilador a gerar o ponto de entrada do programa como um método que tem o modificador async e retorna um System.Threading.Tasks.Task. Esse programa não retorna um valor, portanto, o ponto de entrada do programa retorna um Task. Se o programa retornar um valor inteiro, você adicionará uma instrução return ao final de suas instruções de nível superior. Essa instrução return especificaria o valor inteiro a ser retornado. Se suas instruções de nível superior incluírem uma expressão await, o tipo de retorno se tornará System.Threading.Tasks.Task<TResult>.

Refatoração para o futuro

O programa deverá ser semelhante ao seguinte código:

Console.WriteLine();
foreach(var s in args)
{
    Console.Write(s);
    Console.Write(' ');
}
Console.WriteLine();

for (int i = 0; i < 20; i++)
{
    Console.Write("| -");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("/ \\");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("- |");
    await Task.Delay(50);
    Console.Write("\b\b\b");
    Console.Write("\\ /");
    await Task.Delay(50);
    Console.Write("\b\b\b");
}
Console.WriteLine();

string[] answers =
[
    "It is certain.",       "Reply hazy, try again.",     "Don't count on it.",
    "It is decidedly so.",  "Ask again later.",           "My reply is no.",
    "Without a doubt.",     "Better not tell you now.",   "My sources say no.",
    "Yes – definitely.",    "Cannot predict now.",        "Outlook not so good.",
    "You may rely on it.",  "Concentrate and ask again.", "Very doubtful.",
    "As I see it, yes.",
    "Most likely.",
    "Outlook good.",
    "Yes.",
    "Signs point to yes.",
];

var index = new Random().Next(answers.Length - 1);
Console.WriteLine(answers[index]);

O código anterior é razoável. Funciona. Mas não é reutilizável. Agora que o aplicativo está funcionando, é hora de retirar as partes reutilizáveis.

Um candidato é o código que exibe a animação de espera. Esse snippet pode se tornar um método:

Você pode começar criando uma função local em seu arquivo. Substitua a animação atual pelo seguinte:

await ShowConsoleAnimation();

static async Task ShowConsoleAnimation()
{
    for (int i = 0; i < 20; i++)
    {
        Console.Write("| -");
        await Task.Delay(50);
        Console.Write("\b\b\b");
        Console.Write("/ \\");
        await Task.Delay(50);
        Console.Write("\b\b\b");
        Console.Write("- |");
        await Task.Delay(50);
        Console.Write("\b\b\b");
        Console.Write("\\ /");
        await Task.Delay(50);
        Console.Write("\b\b\b");
    }
    Console.WriteLine();
}

O código anterior cria uma função local no método principal. Ainda não é reutilizável. Então, extraia esse código em uma classe. Crie um novo arquivo chamado utilities.cs e adicione o seguinte código:

namespace MyNamespace
{
    public static class Utilities
    {
        public static async Task ShowConsoleAnimation()
        {
            for (int i = 0; i < 20; i++)
            {
                Console.Write("| -");
                await Task.Delay(50);
                Console.Write("\b\b\b");
                Console.Write("/ \\");
                await Task.Delay(50);
                Console.Write("\b\b\b");
                Console.Write("- |");
                await Task.Delay(50);
                Console.Write("\b\b\b");
                Console.Write("\\ /");
                await Task.Delay(50);
                Console.Write("\b\b\b");
            }
            Console.WriteLine();
        }
    }
}

Um arquivo com instruções de nível superior também pode conter namespaces e tipos no final do arquivo, após as instruções de nível superior. Porém, para este tutorial, você coloca o método de animação em um arquivo separado para torná-lo mais facilmente reutilizável.

Por fim, é possível limpar o código de animação para remover algumas duplicações:

foreach (string s in animations)
{
    Console.Write(s);
    await Task.Delay(50);
    Console.Write("\b\b\b");
}

Agora você tem um aplicativo completo e refatorou as partes reutilizáveis para uso posterior. Você pode chamar o novo método utilitário de suas instruções de nível superior, conforme exibido abaixo na versão final do programa principal:

using MyNamespace;

Console.WriteLine();
foreach(var s in args)
{
    Console.Write(s);
    Console.Write(' ');
}
Console.WriteLine();

await Utilities.ShowConsoleAnimation();

string[] answers =
[
    "It is certain.",       "Reply hazy, try again.",     "Don’t count on it.",
    "It is decidedly so.",  "Ask again later.",           "My reply is no.",
    "Without a doubt.",     "Better not tell you now.",   "My sources say no.",
    "Yes – definitely.",    "Cannot predict now.",        "Outlook not so good.",
    "You may rely on it.",  "Concentrate and ask again.", "Very doubtful.",
    "As I see it, yes.",
    "Most likely.",
    "Outlook good.",
    "Yes.",
    "Signs point to yes.",
];

var index = new Random().Next(answers.Length - 1);
Console.WriteLine(answers[index]);

O exemplo anterior adiciona a chamada Utilities.ShowConsoleAnimation e uma instrução adicional using.

Resumo

As instruções de nível superior facilitam a criação de programas simples para uso para explorar novos algoritmos. É possível experimentar algoritmos tentando diferentes snippets de código. Após verificar o que funciona, é possível refatorar o código para ser mais sustentável.

As instruções de nível superior simplificam programas baseados em aplicativos de console. Elas incluem funções do Azure, ações do GitHub e outros utilitários pequenos. Para obter mais informações, consulte Instruções de nível superior (Guia de Programação em C#).