C# 9.0 – Specification – Top-Level Statements
Índice general – C# 9.0 – Specification
Introducción
La característica de C# 9 sobre la que voy a hablar en esta entrada es algo particular. De hecho, ha sido algo controvertida, y detrás de ella, parece estar la intención de acercar C# a otros lenguajes de programación, aunque su alcance tiene que ver únicamente con aplicaciones de consola.
Veámosla…
Aplicación de consola tradicional
Lo primero que haremos será recordar la típica aplicación de consola.
Algo así como:
using System; public class Program { public static void Main(string[] args) { Console.WriteLine("Hello, world!"); } }
O también:
public class Program { public static void Main(string[] args) { System.Console.WriteLine("Hello, world!"); } }
O rizando el rizo aún más:
public class Program { public static void Main(string[] args) => System.Console.WriteLine("Hello, world!"); }
Todas ellas válidas, tienen algo en común.
Tienen un método Main que es el método de entrada de la aplicación de consola.
Es el método que se ejecuta en primer lugar al ejecutar nuestra aplicación de consola.
Nada nuevo supongo…
Main en una aplicación de consola es opcional en C# 9
La novedad en C# 9, es que el método de entrada Main, es ahora un método opcional.
Es decir, podemos escribir nuestra aplicación de consola sin este método.
Por decirlo de una forma, en C# 9 el código anterior podría quedar ahora algo así como:
using System; Console.WriteLine("Hello, world!");
O también:
System.Console.WriteLine("Hello, world!");
Su «traducción» por debajo
Ahora bien, esto «por debajo» ¿cómo es?.
El último ejemplo, tendría el siguiente aspecto «por dentro»:
using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Security; using System.Security.Permissions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [CompilerGenerated] internal static class $ { private static void $(string[] args) { Console.WriteLine("Hello, world!"); } }
Es decir, se está creando lo mismo que escribíamos en C# 8 ó anteriores, pero por nosotros.
Está creando su método de entrada Main, y está creando los using que corresponden por nosotros.
También está creando como parámetro de entrada argumentos, algo que veremos a continuación con algo más de detalle.
En resumen. Hemos simplificado la cantidad de código escrito, pero no hay más magia que esa.
Uso de argumentos en Top-Level Statements
Dentro de nuestro código, podemos gestionar de forma interna la entrada de argumentos.
De hecho, un código general en el que se espera argumentos y trata el primero de ellos por ejemplo, quedaría de esta forma:
var param = System.String.Empty; if (args.Length is > 0) param = args[0]; System.Console.WriteLine($"You passed the param '{param}'");
Y esto por «dentro» quedaría de esta forma:
[CompilerGenerated] internal static class $ { private static void $(string[] args) { string str = string.Empty; if (args.Length > 0) { str = args[0]; } Console.WriteLine("You passed the param '" + str + "'"); } }
Funciones ejecutadas dentro de la aplicación de consola
Otra característica es la posibilidad de crear funciones dentro de la aplicación de consola y ejecutarlas como un todo en uno.
Es decir, podríamos programar nuestro código de esta forma:
var name = "c#"; System.Console.WriteLine($"{NameToUpperCase(name)}"); string NameToUpperCase(string name) => name.ToUpper();
Y su traducción interna quedaría de esta otra manera:
[CompilerGenerated] internal static class $ { private static void $(string[] args) { string name = "c#"; Console.WriteLine(<$>g__NameToUpperCase|0_0(name) ?? ""); } internal static string <$>g__NameToUpperCase|0_0(string name) { return name.ToUpper(); } }
Clases dentro de nuestra aplicación de consola
Y de igual manera, cuando trabajamos con clases dentro de nuestra aplicación de consola.
De hecho, un código de ejemplo de esto sería el siguiente:
var name = "c#"; var sample = new Sample(); System.Console.WriteLine($"{sample.NameToUpperCase(name)}"); public class Sample { public string NameToUpperCase(string name) => name.ToUpper(); }
Y su conversión quedaría de esta otra manera:
[CompilerGenerated] internal static class $ { private static void $(string[] args) { string name = "c#"; Sample sample = new Sample(); Console.WriteLine(sample.NameToUpperCase(name) ?? ""); } } public class Sample { public string NameToUpperCase(string name) { return name.ToUpper(); } }
Es decir, internamente ha sabido separar la clase principal de Main y la que hemos declarado en sus correspondientes niveles.
Y aunque el código puede quedar algo espagueti, la verdad es que si no es demasiado complejo o grande, se puede interpretar y leer perfectamente.
Conclusiones
Y exactamente igual a lo visto anteriormente, podemos hacer con métodos asíncronos.
Lo que hemos visto, es por lo tanto, una forma de simplificar el código de nuestras aplicaciones de consola como hacen otros lenguajes de programación.
Es, a mi modo de ver, una buena idea para hacer más legible el código y atraer a nuevos desarrolladores o desarrolladores que deseen aprender C#, pero por el otro lado (y también como opinión personal), ocultan cosas que conviene conocer o tener en consideración como acabo de demostrar. Sobre todo para aquellos desarrolladores no tan acostumbrados al lenguaje o que están empezando con él.
Finalmente, y no lo digo por esta característica de C# 9, sino por cualquier característica que se añade a la especificación (ahora en C# 9 o en especificaciones anteriores del lenguaje), tengo la impresión de que últimamente, la tendencia es satisfacer a quienes quieren escribir menos código, dando por supuesto que menos código es sinónimo de mejor, pero cuidado, menos código no tiene porqué significar tener código de más calidad y ser mejor. Siempre debe existir un equilibrio entre entender lo que hace el código, mantenerlo y extenderlo. Estas nuevas características del lenguaje pueden ser muy interesantes en determinados contextos, pero cuidado con extender su uso y aplicación como un «para todo».
Happy Coding!