Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 19 de octubre de 2021
.NET Core

Hace unos días hablábamos de las directivas using globales, un interesante añadido a C# 10 que permite importar espacios de nombres en todos los archivos de código del proyecto, sin necesidad de repetir cientos de veces las mismas líneas en sus encabezados. Simplemente, si un namespace es interesante para nuestro proyecto, lo declaramos como global en algún punto y será lo mismo que si lo hubiéramos hecho en cada uno de los archivos .cs:

global using System;
global using System.Text;
global using System.Text.Json;
global using MyProject.Model;
...

Bien podían haberlo dejado aquí porque ya es una mejora sustancial respecto a lo que tenemos, pero no, el equipo de diseño de C# sigue introduciendo detalles que pueden hacernos la vida más sencilla. Es el caso de los implicit usings que, de la misma forma, acompañan a .NET 6 y C# 10.

Importación implícita de namespaces

El hecho que hay detrás de esta característica es bien simple: hay ciertos espacios de nombres cuya importación que son casi obligatorios en función del tipo de proyecto que estemos desarrollando. Por ejemplo, en casi todos los proyectos encontraremos referencias a System, System.Collection.Generics o System.Linq. O si programamos una aplicación web ASP.NET Core, es habitual que acabemos importando namespaces como Microsoft.AspNetCore.Builder, Microsoft.AspNetCore.Routing o System.Net.Http.Json.

.NET 6 importará automáticamente ciertos espacios de nombres en función del tipo de proyecto, que viene dado por el SDK definido en la primera línea del archivo .csproj, como el siguiente, correspondiente a un proyecto Web:

<Project Sdk="Microsoft.NET.Sdk.Web">
...  
</Project>

En función de este SDK, se importarán los siguientes namespaces:

SDKNamespaces importados
Microsoft.NET.SdkSystem
System.Collections.Generic
System.IO
System.Linq
System.Net.Http
System.Threading
System.Threading.Tasks
Microsoft.NET.Sdk.WebSystem.Net.Http.Json
Microsoft.AspNetCore.Builder
Microsoft.AspNetCore.Hosting
Microsoft.AspNetCore.Http
Microsoft.AspNetCore.Routing
Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging
(+ todos los de Microsoft.NET.Sdk)
Microsoft.NET.Sdk.Worker Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging
(+ todos los de Microsoft.NET.Sdk)

Para evitar conflictos con clases existentes, esta característica está desactivada por defecto, de forma que si migramos un proyecto de .NET 5 a .NET 6 seguro que no se romperá nada. Pero para proyectos nuevos, ya habilita de serie en las nuevas plantillas, usando la siguiente configuración en el .csproj:

<PropertyGroup>
    <ImplicitUsings>enable</ImplicitUsings>
    ...
</PropertyGroup>    

Internamente, cuando los implicit usings están habilitados, se generará durante la compilación un archivo llamado [ProjectName].GlobalUsings.g.cs en la carpeta obj. Por ejemplo, este es el contenido que podemos encontrar en una aplicación web:

// <auto-generated/>
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Net.Http.Json;
global using global::System.Threading;
global using global::System.Threading.Tasks;

Aunque este proceso sea automático, podemos utilizar el .csproj para ajustar los espacios de nombre incluidos en este archivo, como ya vimos en el post sobre los usings globales. En el siguiente ejemplo vemos cómo podríamos eliminar System y añadir MyProject.Model de forma muy sencilla:

<ItemGroup>
    <Using Remove="System" />
    <Using Include="MyProject.Model" />
</ItemGroup>

En definitiva, la idea no está nada mal y es una de las piezas que permite conseguir algunas cosas realmente espectaculares, como las que podemos ver al crear nuevos proyectos de consola, cuyo código veremos que consiste en una única línea, o las cuatro o cinco líneas que costará poner en marcha un servidor ASP.NET Core. A cambio, eso sí, tendremos que aceptar el cierto grado de "magia" que este tipo de comportamientos  implícitos añaden a nuestras aplicaciones.

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!