Main() y argumentos de línea de comandos

El método Main es el punto de entrada de una aplicación de C# (las bibliotecas y los servicios no requieren un método Main como punto de entrada). Cuando se inicia la aplicación, el método Main es el primero que se invoca.

Solo puede haber un punto de entrada en un programa de C#. Si hay más de una clase que tenga un método Main, deberá compilar el programa con la opción del compilador StartupObject para especificar qué método Main quiere utilizar como punto de entrada. Para obtener más información, consulte StartupObject (opciones del compilador de C#).

class TestClass
{
    static void Main(string[] args)
    {
        // Display the number of command line arguments.
        Console.WriteLine(args.Length);
    }
}

También puede usar instrucciones de nivel superior en un archivo como punto de entrada para la aplicación:

using System.Text;

StringBuilder builder = new();
builder.AppendLine("Hello");
builder.AppendLine("World!");

Console.WriteLine(builder.ToString());

Información general

  • El método Main es el punto de entrada de un programa ejecutable; es donde se inicia y finaliza el control del programa.
  • Main se declara dentro de una clase o estructura. El valor de Main debe ser static y no public. (En el ejemplo anterior, recibe el acceso predeterminado de private). Una marcas envolventes class pueden ser static.
  • Main puede tener un tipo de valor devuelto void, int, Task o Task<int>.
  • Solo si Main devuelve un tipo de valor devuelto Task o Task<int>, la declaración de Main puede incluir el modificador async, Esto excluye específicamente un método async void Main.
  • El método Main se puede declarar con o sin un parámetro string[] que contiene los argumentos de línea de comandos. Al usar Visual Studio para crear aplicaciones Windows, se puede agregar el parámetro manualmente o usar el método GetCommandLineArgs() con el fin de obtener los argumentos de la línea de comandos. Los parámetros se leen como argumentos de línea de comandos indizados con cero. A diferencia de C y C++, el nombre del programa no se trata como el primer argumento de línea de comandos en la matriz args, pero es el primer elemento del método GetCommandLineArgs().

En la lista siguiente se muestran las signaturas Main válidas:

public static void Main() { }
public static int Main() { }
public static void Main(string[] args) { }
public static int Main(string[] args) { }
public static async Task Main() { }
public static async Task<int> Main() { }
public static async Task Main(string[] args) { }
public static async Task<int> Main(string[] args) { }

En los ejemplos anteriores se usa el modificador de acceso public. Esto es habitual, pero no es necesario.

Al agregar los tipos de valor devuelto async, Task y Task<int>, se simplifica el código de programa cuando las aplicaciones de consola tienen que realizar tareas de inicio y await de operaciones asincrónicas en Main.

Valores devueltos Main()

Puede devolver un objeto int desde el método Main si lo define de una de las siguientes maneras:

Código del método Main Signatura de Main
No se usa args ni await static int Main()
Se usa args, no se usa await static int Main(string[] args)
No se usa args, se usa await static async Task<int> Main()
Se usan args y await static async Task<int> Main(string[] args)

Si el valor devuelto de Main no se usa, la devolución de void o Task permite que el código sea ligeramente más sencillo.

Código del método Main Signatura de Main
No se usa args ni await static void Main()
Se usa args, no se usa await static void Main(string[] args)
No se usa args, se usa await static async Task Main()
Se usan args y await static async Task Main(string[] args)

Pero, si se devuelve int o Task<int>, el programa puede comunicar información de estado a otros programas o scripts que invocan el archivo ejecutable.

En el ejemplo siguiente se muestra cómo se puede acceder al código de salida para el proceso.

En este ejemplo se usan las herramientas de línea de comandos de .NET Core. Si no está familiarizado con las herramientas de línea de comandos de .NET Core, puede obtener información sobre ellas en este artículo de introducción.

Cree una aplicación mediante la ejecución de dotnet new console. Modifique el método Main en Program.cs como se indica a continuación:

// Save this program as MainReturnValTest.cs.
class MainReturnValTest
{
    static int Main()
    {
        //...
        return 0;
    }
}

Cuando un programa se ejecuta en Windows, cualquier valor devuelto por la función Main se almacena en una variable de entorno. Esta variable de entorno se puede recuperar mediante ERRORLEVEL desde un archivo por lotes, o mediante $LastExitCode desde PowerShell.

Puede compilar la aplicación mediante el comando dotnet build de la CLI de dotnet.

Después, cree un script de PowerShell para ejecutar la aplicación y mostrar el resultado. Pegue el código siguiente en un archivo de texto y guárdelo como test.ps1 en la carpeta que contiene el proyecto. Ejecute el script de PowerShell. Para ello, escriba test.ps1 en el símbolo del sistema de PowerShell.

Dado que el código devuelve el valor cero, el archivo por lotes comunicará un resultado satisfactorio. En cambio, si cambia MainReturnValTest.cs para que devuelva un valor distinto de cero y luego vuelve a compilar el programa, la ejecución posterior del script de PowerShell informará de que se ha producido un error.

dotnet run
if ($LastExitCode -eq 0) {
    Write-Host "Execution succeeded"
} else
{
    Write-Host "Execution Failed"
}
Write-Host "Return value = " $LastExitCode
Execution succeeded
Return value = 0

Valores devueltos asincrónicos de Main

Cuando se declara un valor devuelto async para Main, el compilador genera el código reutilizable para llamar a métodos asincrónicos en Main. Si no especifica la palabra clave async, debe escribir ese código usted mismo, como se muestra en el siguiente ejemplo. El código del ejemplo garantiza que el programa se ejecute hasta que se complete la operación asincrónica:

class AsyncMainReturnValTest
{
    public static void Main()
    {
        AsyncConsoleWork().GetAwaiter().GetResult();
    }

    private static async Task<int> AsyncConsoleWork()
    {
        // Main body here
        return 0;
    }
}

Este código reutilizable se puede reemplazar por:

class Program
{
    static async Task<int> Main(string[] args)
    {
        return await AsyncConsoleWork();
    }

    private static async Task<int> AsyncConsoleWork()
    {
        // main body here 
        return 0;
    }
}

Una ventaja de declarar Main como async es que el compilador siempre genera el código correcto.

Cuando el punto de entrada de la aplicación devuelve Task o Task<int>, el compilador genera un nuevo punto de entrada que llama al método de punto de entrada declarado en el código de la aplicación. Suponiendo que este punto de entrada se denomina $GeneratedMain, el compilador genera el código siguiente para estos puntos de entrada:

  • static Task Main() hace que el compilador emita el equivalente de private static void $GeneratedMain() => Main().GetAwaiter().GetResult();
  • static Task Main(string[]) hace que el compilador emita el equivalente de private static void $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();
  • static Task<int> Main() hace que el compilador emita el equivalente de private static int $GeneratedMain() => Main().GetAwaiter().GetResult();
  • static Task<int> Main(string[]) hace que el compilador emita el equivalente de private static int $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();

Nota

Si en los ejemplos se usase el modificador async en el método Main, el compilador generaría el mismo código.

Argumentos de la línea de comandos

Puede enviar argumentos al método Main definiéndolo de una de las siguientes maneras:

Código del método Main Signatura de Main
No hay valores devueltos, no se usa await static void Main(string[] args)
Valor devuelto, no se usa await static int Main(string[] args)
No hay valores devueltos, se usa await static async Task Main(string[] args)
Valor devuelto, se usa await static async Task<int> Main(string[] args)

Si no se usan los argumentos, puede omitir args de la signatura del método para simplificar ligeramente el código:

Código del método Main Signatura de Main
No hay valores devueltos, no se usa await static void Main()
Valor devuelto, no se usa await static int Main()
No hay valores devueltos, se usa await static async Task Main()
Valor devuelto, se usa await static async Task<int> Main()

Nota

También puede usar Environment.CommandLine o Environment.GetCommandLineArgs para acceder a los argumentos de la línea de comandos desde cualquier punto de una consola o de una aplicación de Windows Forms. Para habilitar los argumentos de la línea de comandos en la signatura de método Main de una aplicación de Windows Forms, tendrá que modificar manualmente la signatura de Main. El código generado por el diseñador de Windows Forms crea un Main sin ningún parámetro de entrada.

El parámetro del método Main es una matriz String que representa los argumentos de la línea de comandos. Normalmente, para determinar si hay argumentos, se prueba la propiedad Length; por ejemplo:

if (args.Length == 0)
{
    System.Console.WriteLine("Please enter a numeric argument.");
    return 1;
}

Sugerencia

La matriz args no puede ser NULL. así que es seguro acceder a la propiedad Length sin comprobar los valores NULL.

También puede convertir los argumentos de cadena en tipos numéricos mediante la clase Convert o el método Parse. Por ejemplo, la siguiente instrucción convierte la string en un número long mediante el método Parse:

long num = Int64.Parse(args[0]);

También se puede usar el tipo de C# long, que tiene como alias Int64:

long num = long.Parse(args[0]);

También puede usar el método ToInt64 de la clase Convert para hacer lo mismo:

long num = Convert.ToInt64(s);

Para obtener más información, vea Parse y Convert.

Sugerencia

El análisis de argumentos de línea de comandos puede ser complejo. Considere la posibilidad de usar la biblioteca System.CommandLine (actualmente en versión beta) para simplificar el proceso.

En el ejemplo siguiente se muestra cómo usar argumentos de la línea de comandos en una aplicación de consola. La aplicación toma un argumento en tiempo de ejecución, lo convierte en un entero y calcula el factorial del número. Si no se proporciona ningún argumento, la aplicación emite un mensaje en el que se explica el uso correcto del programa.

Para compilar y ejecutar la aplicación desde un símbolo del sistema, siga estos pasos:

  1. Pegue el código siguiente en cualquier editor de texto, y después guarde el archivo como archivo de texto con el nombre Factorial.cs.

    public class Functions
    {
        public static long Factorial(int n)
        {
            // Test for invalid input.
            if ((n < 0) || (n > 20))
            {
                return -1;
            }
    
            // Calculate the factorial iteratively rather than recursively.
            long tempResult = 1;
            for (int i = 1; i <= n; i++)
            {
                tempResult *= i;
            }
            return tempResult;
        }
    }
    
    class MainClass
    {
        static int Main(string[] args)
        {
            // Test if input arguments were supplied.
            if (args.Length == 0)
            {
                Console.WriteLine("Please enter a numeric argument.");
                Console.WriteLine("Usage: Factorial <num>");
                return 1;
            }
    
            // Try to convert the input arguments to numbers. This will throw
            // an exception if the argument is not a number.
            // num = int.Parse(args[0]);
            int num;
            bool test = int.TryParse(args[0], out num);
            if (!test)
            {
                Console.WriteLine("Please enter a numeric argument.");
                Console.WriteLine("Usage: Factorial <num>");
                return 1;
            }
    
            // Calculate factorial.
            long result = Functions.Factorial(num);
    
            // Print result.
            if (result == -1)
                Console.WriteLine("Input must be >= 0 and <= 20.");
            else
                Console.WriteLine($"The Factorial of {num} is {result}.");
    
            return 0;
        }
    }
    // If 3 is entered on command line, the
    // output reads: The factorial of 3 is 6.
    
  2. En la pantalla Inicio o en el menú Inicio, abra una ventana del Símbolo del sistema para desarrolladores de Visual Studio y navegue hasta la carpeta que contiene el archivo que creó.

  3. Escriba el siguiente comando para compilar la aplicación.

    dotnet build

    Si la aplicación no tiene ningún error de compilación, se creará un archivo ejecutable con el nombre Factorial.exe.

  4. Escriba el siguiente comando para calcular el factorial de 3:

    dotnet run -- 3

  5. El comando genera este resultado: The factorial of 3 is 6.

Nota

Al ejecutar una aplicación en Visual Studio, puede especificar argumentos de la línea de comandos en la Página Depuración, Diseñador de proyectos.

Especificación del lenguaje C#

Para obtener más información, consulte la Especificación del lenguaje C#. La especificación del lenguaje es la fuente definitiva de la sintaxis y el uso de C#.

Vea también