Compartir vía


Tutorial: Exploración de ideas mediante instrucciones de nivel superior para compilar código mientras aprende

En este tutorial, aprenderá a:

  • Obtener información sobre las reglas que rigen el uso de las instrucciones de nivel superior.
  • Usar instrucciones de nivel superior para explorar algoritmos.
  • Refactorizar exploraciones en componentes reutilizables.

Requisitos previos

Deberá configurar la máquina para ejecutar .NET 6 o superior. El compilador de C# está disponible a partir de Visual Studio 2022 o del SDK de .NET.

En este tutorial se da por supuesto que conoce bien C# y. NET, incluidos Visual Studio o la CLI de .NET.

Comienzo de la exploración

Las instrucciones de nivel superior permiten evitar la ceremonia adicional que requiere colocar el punto de entrada del programa en un método estático en una clase. El punto de partida típico de una aplicación de consola nueva es similar al código siguiente:

using System;

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

El código anterior es el resultado de ejecutar el comando dotnet new console y crear una aplicación de consola. Estas 11 líneas solo contienen una línea de código ejecutable. Puede simplificar ese programa con la nueva funcionalidad de instrucciones de nivel superior. Esto le permite quitar todas las líneas de este programa menos dos:

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

Importante

Las plantillas de C# para .NET 6 usan instrucciones de nivel superior. Es posible que la aplicación no coincida con el código de este artículo si ya ha actualizado a .NET 6. Para obtener más información, consulte el artículo Las nuevas plantillas de C# generan instrucciones de nivel superior.

El SDK de .NET 6 también agrega un conjunto de directivas implícitasglobal using para proyectos que usan los SDK siguientes:

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

Estas directivas de global using implícitas incluyen los espacios de nombres más comunes para el tipo de proyecto.

Para saber más, consulte el artículo sobre las directivas de uso implícito

Esta característica simplifica la exploración de nuevas ideas. Puede usar las instrucciones de nivel superior para escenarios de scripting o para explorar. Una vez que conozca los aspectos básicos, puede empezar a refactorizar el código y crear métodos, clases u otros ensamblados para los componentes reutilizables que ha compilado. Las instrucciones de nivel superior permiten una experimentación rápida y tutoriales para principiantes. También proporcionan una transición fluida de la experimentación a programas completos.

Las instrucciones de nivel superior se ejecutan en el orden en el que aparecen en el archivo. Las instrucciones de nivel superior solo se pueden usar en un archivo de código fuente de la aplicación. El compilador genera un error si se usan en más de un archivo.

Creación de un contestador automático de .NET mágico

En este tutorial, se creará una aplicación de consola que responde a una pregunta de tipo "sí" o "no" con una respuesta aleatoria. Desarrollas la funcionalidad paso a paso. Puede centrarse en la tarea en lugar de la ceremonia necesaria para la estructura de un programa típico. Después, una vez que esté satisfecho con la funcionalidad, puede refactorizar la aplicación como considere oportuno.

Un buen punto de partida es escribir la pregunta de nuevo en la consola. Puede empezar por escribir el código siguiente:

Console.WriteLine(args);

No declaras una variable args. Para el archivo de origen único que contiene las instrucciones de nivel superior, el compilador reconoce args para indicar los argumentos de la línea de comandos. El tipo de 'args' es un string[], como sucede en todos los programas C#.

Puede ejecutar el comando dotnet run siguiente para probar el código:

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

Los argumentos después de -- en la línea de comandos se pasan al programa. Puede ver el tipo de la variable args impresa en la consola:

System.String[]

Para escribir la pregunta en la consola, tendrá que enumerar los argumentos y separarlos con un espacio. Reemplace la llamada a WriteLine por el código siguiente:

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

Ahora, al ejecutar el programa, mostrará correctamente la pregunta como una cadena de argumentos.

Respuesta con una respuesta aleatoria

Después de repetir la pregunta, puede agregar el código para generar la respuesta aleatoria. Para empezar, agregue una matriz de respuestas posibles:

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.",
];

Esta matriz tiene 10 respuestas que son afirmativas, cinco inexpresivas y cinco negativas. A continuación, agregue el código siguiente para generar y mostrar una respuesta aleatoria de la matriz:

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

Puede volver a ejecutar la aplicación para ver los resultados. Debería ver algo parecido al siguiente resultado:

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.

El código para generar una respuesta incluye una declaración de variable en las instrucciones de nivel superior. El compilador incluye esa declaración en el método Main generado por el compilador. Dado que estas declaraciones de variables son variables locales, no se puede incluir el modificador static.

Este código responde a las preguntas, pero agreguemos una característica más. Quiere que la aplicación de preguntas simule que se piensa la respuesta. Puede hacerlo si agrega una animación de ASCII y se detiene mientras trabaja. Agregue el código siguiente después de la línea que reproduce la pregunta:

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();

También tendrá que agregar una directiva using a la parte superior del archivo de código fuente:

using System.Threading.Tasks;

Las directivas using deben aparecer antes que cualquier otra del archivo. De lo contrario, es un error del compilador. Puede volver a ejecutar el programa y ver la animación. Eso mejora la experiencia. Experimente con la duración del retraso hasta que le guste.

El código anterior crea un conjunto de líneas giratorias separadas por un espacio. Al agregar la palabra clave await se le indica al compilador que genere el punto de entrada del programa como un método con el modificador async y que devuelva System.Threading.Tasks.Task. Este programa no devuelve un valor, por lo que el punto de entrada del programa devuelve Task. Si el programa devuelve un valor entero, tendría que agregar una instrucción return al final de las instrucciones de nivel superior. Esa instrucción return especificaría el valor entero que se va a devolver. Si las instrucciones de nivel superior incluyen una expresión await, el tipo de valor devuelto se convierte en System.Threading.Tasks.Task<TResult>.

Reestructuración del código para el futuro

El programa debería ser similar al código siguiente:

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]);

En el código anterior es razonable. Funciona. Pero no es reutilizable. Ahora que ya tiene la aplicación en funcionamiento, es momento de extraer los elementos reutilizables.

Un candidato es el código que muestra la animación en espera. Ese fragmento de código se puede convertir en un método:

Puede empezar por crear una función local en el archivo. Reemplace la animación actual por el código siguiente:

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

En el código anterior se crea una función local dentro del método Main. Ese código todavía no es reutilizable. Por tanto, extraiga ese código en una clase. Cree un archivo con el nombre utilities.cs y agregue el código siguiente:

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

Un archivo que tiene instrucciones de nivel superior también puede contener espacios de nombres y tipos al final del archivo, después de las instrucciones de nivel superior. Pero para este tutorial, coloque el método de animación en un archivo independiente para que sea más fácil de usar.

Por último, puede limpiar el código de animación para quitar alguna duplicación mediante el bucle foreach para iterar a través de un conjunto de elementos de animación definidos en el array animations.
El método completo ShowConsoleAnimation después de la refactorización debe ser similar al código siguiente:

public static async Task ShowConsoleAnimation()
{
    string[] animations = ["| -", "/ \\", "- |", "\\ /"];
    for (int i = 0; i < 20; i++)
    {
        foreach (string s in animations)
        {
            Console.Write(s);
            await Task.Delay(50);
            Console.Write("\b\b\b");
        }
    }
    Console.WriteLine();
}

Ahora tiene una aplicación completa y ha refactorizado los elementos reutilizables para su uso posterior. Puede llamar al nuevo método de utilidad desde las instrucciones de nivel superior, tal como se muestra en la versión finalizada del 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]);

En el ejemplo anterior se agrega la llamada a Utilities.ShowConsoleAnimation y se agrega otra directiva using.

Resumen

Las instrucciones de nivel superior facilitan la creación de programas sencillos para explorar nuevos algoritmos. Puede experimentar con algoritmos si prueba otros fragmentos de código. Una vez que haya aprendido lo que funciona, puede refactorizar el código para que sea más fácil de mantener.

Las instrucciones de nivel superior simplifican los programas basados en aplicaciones de consola. Estas aplicaciones incluyen Azure Functions, las acciones de GitHub y otras utilidades pequeñas. Para obtener más información, vea Instrucciones de nivel superior (Guía de programación de C#).