La versión 10 del lenguaje C# trae muchas nuevas características pensadas para facilitarnos la vida y ahorrar tiempo a la hora de programar. Hay un par de características nuevas que nos permiten omitir los típicos using
que van al principio de los archivos. Lo cual tiene sus cosas buenas y sus cosas malas...
Primero, por si lo necesitas, repasaremos qué son los using
y luego te cuento cómo funcionan en C# 10, por escrito y en un vídeo...
¿Qué es un "using"?
La plataforma .NET está formada por decenas de miles de clases que se encargan de proporcionarnos decenas de miles de capacidades. Por ejemplo ¿quieres trabajar con cadenas? Pues hay clases especializadas en eso. ¿Necesitas hacer criptografía? Por supuesto que hay clases que te lo dan hecho. ¿Acceso a una base de datos? ¿Enviar un email? ¿Leer y escribir archivos? ¿manejar datos XML?... Casi cualquier cosa que se te ocurra la tienes ya hecha en la plataforma. De hecho, uno de los retos de aprender .NET (o, en realidad, cualquier otra plataforma) es aprender cuáles son sus posibilidades y luego saber cómo "juntar las piezas" para darle una nueva utilidad para lo que estés creando con ella.
Bien, pues todas esas piezas hay que hacerlas accesibles a los programadores. Para lograrlo, la plataforma .NET agrupa las clases en categorías y subcategorías dentro de lo que se llaman espacios de nombres.
Los espacios de nombres son una manera de agrupar clases. De hecho, cuando creas un programa propio también tienes que indicar en qué espacio de nombres lo estás creando, así como todas las clases que hay definidas en el mismo. De este modo te aseguras de que puedes encontrar de manera cómoda y rápida cualquier código que necesites.
Veámoslo con un ejemplo. En .NET, la funcionalidad relacionada con el manejo de archivos está bajo el espacio de nombres System.IO
que tiene un montón de clases diferentes para leer y escribir archivos, manejar streams de datos, etc... Dentro de este espacio de nombres existen a su vez otros más especializados para agrupar otras clases hacer otras cosas más especializadas relacionadas con la entrada/salida, como por ejemplo manejo de archivos en memoria (System.IO.MemoryMappedfiles
), comunicación entre procesos mediante canalizaciones (System.IO.Pipes
), etc, etc...:
Lo mismo ocurre con todo lo demás... Las decenas de clases especializadas en criptografía las encontramos en el espacio de nombres System.Security.Cryptography
, que también tiene otros más especializados dentro y, como puedes deducir, está a su vez en System.Security
que tiene más cosas relacionadas con la seguridad, y dentro de ahí hay muchas otras cosas, organizadas por temas:
Así, está todo en .NET. Y es necesario saber dónde están las clases para poder utilizarlas. Puedes pensar en un espacio de nombres como en una especie de carpeta o cajón que agrupa dentro clases (no es técnicamente así, pero la figura mental te puede ayudar a entenderlo mejor).
O sea, en .NET todo lo que podemos usar está recogido en alguna clase, y esta a su vez está en un espacio de nombres, organizada de manera que sea (más o menos) fácil de localizar.
A la hora de utilizar una clase para algo tenemos tres opciones:
- Si es una clase que está dentro del mismo espacio de nombres de nuestro programa (normalmente, una clase nuestra), podemos usar su nombre sin más. En todos los demás casos, hay que hacer algo de las dos siguientes opciones.
- Escribir su espacio de nombres completo delante del nombre de la clase. Por ejemplo,
System.Security.Cryptography.AesManaged
para poder utilizar la clase AesManaged
que implementa con C# el algoritmo de criptografía AES. Este es el nombre completo (cualificado) de esa clase.
- Utilizar un
using
en el archivo donde queremos utilizar la clase, indicando el espacio de nombres en el que se encuentra. En el ejemplo anterior sería poner al principio del archivo using System.Security.Cryptography;
. A partir de ese momento, en el código de ese archivo podemos simplemente escribir AesManaged
para poder instanciar un objeto de esa clase y poder utilizarlo, sin tener que escribir todo el rato el nombre cualificado de la clase, que es un verdadero tedio si hay que hacerlo muchas veces, además de que dificulta la lectura del código.
Así, por ejemplo, cuando creas una nueva aplicación de consola en .NET 5 o anterior, verás que tienes al principio de tu archivo lo siguiente:
using System;
Esto es porque dentro de este espacio de nombres están todos los tipos básicos que vas a utilizar, así como algunas clases especializadas, como Console
, que te permite enviar y recibir mensajes desde la terminal, además de muchas otras cosas. Como System
es un espacio de nombres tan básico, en realidad es recomendable incluirlo en todos los archivos de código que tengamos, porque sin él tendríamos que estar prefijando con System
a un montón de clases y tipos de datos que usamos a todas horas.
Generalmente, cuando creamos una aplicación nos suele pasar lo mismo: siempre hay una serie de espacios de nombres que solemos utilizar a lo largo de toda la aplicación y que es un engorro tener que estar escribiendo todo el rato al principio de nuestros archivos de código C#.
Nota: alguien me ha comentado en el vídeo que la palabra clave global
ya existía antes de C# 10. En realidad es cierto, pero no tiene nada que ver con lo que se explica en este artículo + vídeo. A lo que se refiere global
en ese caso es al espacio de nombres global o raíz de todos los espacios de nombres, y este "alias" se utiliza siempre con el operador específico ::
(con otros alias de espacios de nombres también) para evitar confusiones en caso de que se nos ocurra definir una ruta de espacio de nombres y un nombre de clases de coincida con el de una clase del sistema, algo que no deberíamos hacer nunca (o al menos no se me ocurre una buena razón para hacerlo), pero no tiene nada que ver con esto. En la referencia de C# lo puedes encontrar explicado si te interesa.
Usings globales
A partir de C# 10 es posible definir usings de manera global. Esto es súper útil porque, como hemos visto, existen ciertos using
que, hasta ahora, teníamos que repetir una y otra vez en todos los archivos de código.
Lo único que tenemos que hacer es poner la palabra global
delante de un using
al principio de un archivo, así:
global using System;
y el espacio de nombres indicado estará disponible automáticamente en todo el proyecto, sin necesidad de escribirlo en cada archivo de código.
Ten en cuenta un par de cosas:
- Puedes usar
global
con cualquier tipo de using
que utilices, incluyendo los estáticos o los alias.
- Los
global using
que incluyas deben estar al principio del archivo y antes de cualquier using
convencional (local al archivo) que quieras incluir.
Aunque puedes meterlos en el archivo o archivos que quieras, mi recomendación sería que uses un único archivo en la raíz, por ejemplo, globalusings.cs
, con el único objeto de contener este tipo de instrucciones. Sino corres el riesgo de que sea un descontrol. De este modo solo tienes que ir a un sitio para añadirlos o cambiarlos.
Usings implícitos
Como decía hace un momento, según el tipo de proyecto que crees es habitual que tengas que utilizar un montón de "usings" todo el rato. Aunque puedes solventarlo con lo anterior, en .NET 6 han introducido una nueva característica llamada "Usings implícitos" que aún nos facilita más la vida, a costa de perder un poco de control.
Para activarla tienes que abrir tu archivo de proyecto (nombreDeTuProyecto.cs
) e incluir un nodo <ImplicitUsings>
con el valor enable
. Así:
Al hacer esto, según el tipo de proyecto que estés creando, se habilitarán automáticamente una serie de usings globales para ti, sin que tengas que hacer nada. ¿Cuáles? Pues eso depende del tipo de proyecto: han elegido los que han creído más interesantes para cada uno, y puede cambiar en el futuro.
Por suerte es muy fácil averiguar cuáles son, ya que cuando compilamos la aplicación, en la carpeta obj
(que es donde se guardan los resultados intermedios de la compilación) tenemos un archivo específico que encontraremos en la subcarpeta Debug/net6.0
llamado nombreDeTuProyecto.GlobalUsings.g.cs
. Si lo abres verás qué usings
exactamente han incluido. En este, por ejemplo, verás los de una aplicación de consola:
Como ves, en este caso, incluye el espacios de nombres System
, el de manejo de colecciones genéricas, el de operaciones de entrada y salida, el de Linq, el de comunicaciones por HTTP, el de multisubproceso (hilos) y el de tareas (para operaciones asíncronas), que son los que querrás usar en muchas ocasiones en una aplicación de consola. Así que ya no tendrás que incluirlos manualmente nunca más.
No puedes cambiarlos tocando ese archivo (en el vídeo que acompaña al artículo te cuento cómo modificarlos usando el archivo de proyecto): ni reducirlos ni aumentarlos, pero como tampoco hace daño tenerlos ni enlentece la aplicación, no pasa nada. Por otro lado, los que te falten los puedes incluir por tu cuenta en un archivo global, como ya hemos visto.
En general pienso que son una característica interesante aunque, como todo "azúcar sintáctico", hace que pierdas un poco de control y esconde complejidades para los más novatos que pueden hacer que pierdan de vista algunas cosas importantes. Está bien para facilitar el comienzo de los más novatos en la plataforma, pero es un arma de doble filo: también facilita que aprendan perdiendo algunos conceptos de vista que son muy importantes.
¡Espero que te sea útil!