Share via



Julio de 2019

Volumen 34, número 7

[C#]

.NET reunificado: planes de Microsoft para .NET 5

Por Mark Michaelis | Julio de 2019

Cuando Microsoft anunció .NET 5 en Microsoft Build 2019 en mayo, marcó un importante paso hacia delante para los desarrolladores que trabajan con plataformas de escritorio, web, móviles, de nube y dispositivos. De hecho, .NET 5 es esa actualización de plataforma poco frecuente que unifica marcos divergentes, reduce la complejidad del código y mejora considerablemente el alcance multiplataforma.

No se trata de una tarea nimia. Microsoft propone combinar las secuencias de código fuente de varios marcos clave: .NET Framework, .NET Core y Mono/Xamarin. El esfuerzo también unificará los subprocesos que se separaron a principios de siglo y ofrecerá a los desarrolladores una plataforma de destino para su trabajo.

El concepto de flujo de código fuente de la Figura 1 muestra cómo la escala de tiempo de cada marco se sincroniza y, finalmente, se combina en un único subproceso a partir de .NET 5 en noviembre de 2020. (Tenga en cuenta que .NET Framework se ha abreviado como .NET FW en la imagen). Tras su lanzamiento, .NET 5 eclipsará .NET Framework 4.8, Mono 5.0 y .NET Core 3.0.

Concepto de flujo de código fuente de .NET, Mono y la iniciativa de código fuente compartido para .NET 5
Figura 1 Concepto de flujo de código fuente de .NET, Mono y la iniciativa de código fuente compartido para .NET 5 (haga clic para la versión ampliada)

Ciertamente, la Figura 1 es más conceptual que la realidad, con código fuente que se bifurca (y más probablemente solo se copia) en lugar de ramificarse, y características (como Windows Presentation Foundation [WPF] o Windows Forms) que se migran en lugar de combinarse. No obstante, la infografía proporciona una vista bastante transparente del historial ancestral del código fuente de .NET, que muestra su evolución desde las tres ramas principales hasta .NET 5.

El resultado de este trabajo es una plataforma unificada con el marco .NET 5, que se ejecuta en todas las plataformas (escritorio, web, nube, móvil, etc.). La Figura 2 representa esta arquitectura unificada.

.NET 5: una plataforma unificada
Figura 2 .NET 5: una plataforma unificada

Historia del origen

Resulta fascinante cómo las distintas versiones de .NET framework, como las de Shared Source Common Initiative (Rotor) de Microsoft, SilverLight, Windows Phone, .NET Core y .NET Framework (pero no Mono), se compilaron originalmente a partir del mismo código fuente. En otras palabras, todo el código fuente se mantenía en un único repositorio y lo compartían todas las versiones de .NET framework. De este modo, Microsoft pudo garantizar que las API que eran comunes para diferentes marcos procedieran del mismo código fuente y tuvieran las mismas firmas. Las únicas diferencias eran las API que se compartían. (Observe el uso de .NET "framework" en minúsculas, que hace referencia a todas las versiones de .NET Framework en general, frente a .NET "Framework" en mayúsculas, que hace referencia a Windows .NET Framework, como .NET Framework 4.8).

Para lograr un único origen orientado a marcos distintos, se usaron diferentes técnicas de creación de un subconjunto, como un gran número de #ifdefs. También resulta interesante cómo un conjunto ortogonal de técnicas de creación de subconjuntos (es decir, distintos #ifdefs) se forjaron en el código fuente de .NET para dar lugar a la compatibilidad multiplataforma de Rotor, lo que permitió el desarrollo rápido de Silverlight y (mucho más tarde) de .NET Core a partir de la misma base de código.

Mientras que el conjunto ortogonal de técnicas de creación de subconjuntos se mantiene, el que permite la compilación multiplataforma (la creación de subconjuntos para producir marcos diferentes) se está quitando. (Consulte, por ejemplo, la solicitud de incorporación de cambios en bit.ly/2WdSzv2, que quita varias marcas #ifdefs obsoletas). El motivo por el que hoy se pueden quitar es que, unos días después del lanzamiento de .NET 1.1, se bifurcó (más bien, copió) el código fuente de .NET Core y .NET Framework. Esto, en realidad, explica por qué hay dos sitios web de código de fuente de .NET diferentes: .NET Framework en referencesource.microsoft.com y .NET Core en source.dot.net. Son dos bases de código separadas.

La decisión de realizar la bifurcación en lugar de continuar usando la creación de subconjuntos refleja la tensión entre mantener la compatibilidad con versiones anteriores (prioridad alta para .NET Framework) y la innovación (prioridad de .NET Core). Simplemente, había demasiados casos en que mantener la compatibilidad entraba en conflicto con la corrección o la mejora de las API de .NET, de modo que el código fuente tenía que separarse si ambos objetivos eran necesarios. El uso de marcas #ifdefs ya no era una manera funcional de separar los marcos cuando, de hecho, las API eran diferentes y la versión era incompatible.

Con el tiempo, sin embargo, surgió otro conflicto: permitir a los desarrolladores crear bibliotecas que pudieran ejecutarse correctamente en ambos marcos. Para lograrlo, se necesitaba un estándar de API a fin de asegurar a los desarrolladores que un marco tendría el conjunto concreto de API que hubiera especificado el estándar. De este modo, al aprovechar solo las API dentro del estándar, su biblioteca era compatible con los distintos marcos (el mismo ensamblado exacto podía ejecutarse en distintos marcos de trabajo sin tener que volver a realizar la compilación).

Con cada nueva característica agregada a .NET (por ejemplo Span<T>), se hizo cada vez más difícil mantener la compatibilidad con versiones anteriores del marco. Más concretamente, el reto era admitir los conceptos en nuevas versiones de .NET Standard, que eran, en realidad, nuevas innovaciones de .NET Core, en .NET Framework. Además, aunque de manera menos significativa, cada nueva versión de .NET Standard incluía un conjunto de API cada vez mayor, hasta que mantener la compatibilidad de .NET Standard entre .NET Framework y .NET Core se convirtió en una carga de mantenimiento.

Juntémonos

Los dos marcos empezaron a parecerse cada vez más debido al estándar. A medida que las API eran más coherentes, comenzó a surgir la pregunta obvia: ¿Por qué no volver a unir las bases de código separadas? De hecho, a partir de .NET Core 3.0 Preview, gran parte de la API de Windows y WPF de .NET Framework era de selección exclusiva y estaba combinada en la base de código de .NET Core 3.0, que es exactamente lo que ocurrió. El código fuente de .NET Core 3.0 se convirtió en uno solo con la funcionalidad actual (escritorio, nube, móvil e IoT) en .NET Framework 4.8.

Llegado este punto, todavía queda un marco de .NET framework importante que no he tratado: Mono/Xamarin. Aunque el código fuente de Rotor estaba disponible públicamente, usarlo habría infringido el contrato de licencia. En su lugar, Mono empezó como un esfuerzo de desarrollo diferente y totalmente nuevo con la perspectiva de crear una versión de .NET compatible con Linux. El marco Mono continuó creciendo con el tiempo, hasta que Novell adquirió la empresa (Ximian) en 2003, que se cerró ocho años más tarde con la venta de Novell a Attachmate. La administración de Ximian se reformó rápidamente en mayo de 2011 como Xamarin. Menos de dos años más tarde, Xamarin desarrolló una base de código de interfaz de usuario multiplataforma que se ejecutaba en Android e iOS, y aprovechaba una versión multiplataforma de código cerrado de Mono en segundo plano.

En 2016, Microsoft adquirió Xamarin para poner todo el código fuente de .NET framework bajo el control de una sola empresa. Poco después, Mono y el SDK de Xamarin se lanzaban como código abierto.

Esto nos lleva hasta donde nos encontramos en la primera mitad de 2019, básicamente con dos bases de código principales a partir de entonces: .NET Core 3.0 y Mono/Xamarain. (Aunque Microsoft admitirá .NET Framework 4.8 en Windows durante cualquier período previsible, .NET Core 3.0 y, posteriormente, .NET 5, lo eclipsarán como plataforma estratégica para las nuevas aplicaciones en adelante). Además, se incluyen .NET Standard y la unión de las API en la versión de .NET Standard 2.1 que se lanzará en breve.

Nuevamente, surge la pregunta siguiente: con las API cada vez más próximas, ¿no se puede combinar .NET Core 3.0 con Mono? Este es un esfuerzo que, de hecho, ya se ha iniciado. Hoy en día, Mono es ya un tercio del origen de Mono, un tercio de CoreFx y un tercio del origen de referencia de .NET Framework. Esto preparó el camino para .NET 5, anunciado en Microsoft Build 2019.

Ventajas de .NET 5

Esta versión unificada de .NET 5 será compatible con todos los tipos de aplicaciones de .NET: Xamarin, ASP.NET, IoT y de escritorio. Además, utilizará una única biblioteca de clases base (BCL)/CoreFX, dos tiempos de ejecución independientes y bases de código de tiempo de ejecución (porque es muy difícil usar un origen único para dos tiempos de ejecución destinados a ser muy diferentes) y una cadena de una sola herramienta (por ejemplo, la CLI de dotnet). El resultado será uniformidad en todos los comportamientos, las API y las experiencias de desarrollador. Por ejemplo, en lugar de tener tres implementaciones de las API System.*, habrá un único conjunto de bibliotecas que se ejecutará en cada una de las diferentes plataformas.

La unificación de .NET presenta numerosas ventajas. Al unificar el marco, los tiempos de ejecución y los conjuntos de herramientas de desarrollador en una única base de código se reducirá la cantidad de código duplicado que los desarrolladores (tanto de Microsoft como de la comunidad) tendrán que mantener y expandir. Además, como se espera de Microsoft hoy en día, todo el código fuente de .NET 5 será código abierto.

Con la fusión, muchas de las características exclusivas de cada marco individual pasarán a estar disponibles en todas las plataformas. Por ejemplo, los tipos .csproj para estas plataformas se unificarán en el apreciado y sencillo formato de archivo .csproj de .NET Core. Por lo tanto, un tipo de proyecto de .NET Framework podrá aprovechar las ventajas del formato de archivo .csproj de .NET Core. Mientras que una conversión a los formatos de archivo .csproj de .NET Core es necesaria para los archivos .csproj de Xamarin y .NET Framework (incluidos WPF y Windows Forms), la tarea es similar a la conversión de ASP.NET a ASP.NET Core. Afortunadamente, hoy es aún más fácil gracias a herramientas como ConvertProjectToNETCore3 (consulte bit.ly/2W5Lk3D).

Otra área con una diferencia significativa es el comportamiento del tiempo de ejecución de Xamarin y .NET Core/.NET Framework. El primero usa un modelo de compilación estático con la compilación Ahead Of Time (AOT), que compila el código fuente hasta el código fuente nativo de la plataforma. En cambio, .NET Core y .NET Framework usan la compilación Just-In-Time (JIT). Afortunadamente, con .NET 5 se admitirán ambos modelos según el destino del tipo de proyecto.

Por ejemplo, puede optar por compilar el proyecto de .NET 5 en un único archivo ejecutable que use el compilador JIT (vibración) en tiempo de ejecución, o un compilador nativo para que funcione en plataformas Android o iOS. La mayoría de los proyectos usarán la vibración, pero para iOS, todo el código es AOT. Para Blazor del lado cliente, el tiempo de ejecución es el ensamblado web (WASM) y Microsoft pretende usar la compilación AOT para una pequeña cantidad de código administrado (de 100 kb a 300 kb aproximadamente), mientras que el resto se interpretará. (El código AOT es grande, por lo que el costo de conexión supone una carga considerable).

En .NET Core 3.0 puede realizar la compilación en un solo archivo ejecutable, pero dicho ejecutable es, en realidad, una versión comprimida de todos los archivos necesarios para la ejecución en tiempo de ejecución. Cuando se ejecuta el archivo, primero se expande en un directorio temporal y, a continuación, ejecuta el punto de entrada de la aplicación desde el directorio que contiene todos los archivos. Por el contrario, .NET 5 creará un archivo ejecutable único real, que se puede ejecutar directamente in situ.

Otra característica notable de .NET 5 es la interoperabilidad con el código fuente de Java y Objective-C (incluido Swift). Esta ha sido una característica de Xamarin desde las primeras versiones, pero se extenderá a todos los proyectos de .NET 5. Por ejemplo, podrá incluir archivos .jar en el archivo .csproj, y podrá realizar llamadas directamente desde el código de .NET al código de Java o de Objective-C. (Lamentablemente, la compatibilidad con Objective-C llegará más tarde que Java). Debe tener en cuenta que la interoperabilidad entre .NET 5 y Java u Objective-C solo está destinada a la comunicación en proceso. Probablemente, la comunicación distribuida a otros procesos en la misma máquina o incluso a procesos en una máquina diferente requerirá serialización en una invocación distribuida basada en REST o RPC.

Qué no incluye .NET 5

Aunque existe un conjunto considerable de API disponibles en .NET 5 Framework, no incluye todo lo que se puede haber desarrollado durante los últimos 20 años. Es razonable prever que se admitirán todas las API que se identifican en .NET Standard 2.1, pero no algunas de las API más "heredadas", que incluyen Web Forms, el servidor de Windows Communication Foundation (WCF) y Windows Workflow. Estas están destinadas a permanecer en .NET Framework solamente. Si desea conseguir la misma funcionalidad en .NET 5, considere la posibilidad de portar estas API de la siguiente manera:

  • ASP.NET Web Forms => ASP.NET Blazor
  • Servidor WCF y comunicación remota => gRPC
  • Windows Workflow (WF) = > Core WF (github.com/UiPath/corewf)

La falta de soporte técnico del servidor de WCF es, sin duda, frustrante para algunos. Sin embargo, Microsoft decidió recientemente lanzar el software con una licencia MIT de código abierto, cuyo destino controla la comunidad (consulte github.com/CoreWCF/CoreWCF). Todavía queda mucho trabajo por hacer para el lanzamiento independientemente de .NET Framework, pero, mientras tanto, están disponibles las API de WCF del lado cliente (consulte github.com/dotnet/wcf).

Declaración de intenciones

Incluso a medida que Microsoft realiza planes para unificar sus marcos para desarrolladores en .NET 5, la compañía ha anunciado que está adoptando una cadencia regular para sus versiones de .NET unificadas (consulte la Figura 3). En adelante, puede esperar que las versiones de disponibilidad general de .NET se lancen el último trimestre de cada año. De estas versiones, la segunda será una versión de soporte técnico a largo plazo (LTS), para la que Microsoft proporcionará soporte técnico durante un período mínimo de tres años o un año después de una versión LTS posterior, el período que sea más largo. En otras palabras, siempre tendrá, como mínimo, tres años para actualizar su aplicación a la siguiente versión LTS. Consulte bit.ly/2Kfkkw0 para obtener más información sobre la directiva de soporte técnico de .NET Core y para saber en qué puede convertirse .NET 5 más allá de la directiva de soporte técnico.

Programación de versiones de .NET
Figura 3 Programación de versiones de .NET

En este momento, .NET 5 solo es un anuncio o, si lo prefiere, una declaración de intenciones. Queda mucho trabajo por hacer. Aún así, el anuncio es extraordinario. Cuando .NET Core se lanzó inicialmente, el objetivo era ofrecer una versión de .NET multiplataforma capaz de resaltar Azure (especialmente los componentes de plataforma como-servicio [PaaS] de Azure y la compatibilidad con .NET en Linux y en los contenedores de Linux).

En el concepto original, la idea de que todo .NET Framework se podía portar a .NET Core no se consideró realista. Al acercarse el lanzamiento de .NET Core 2.0, esto comenzó a cambiar. Microsoft se dio cuenta de que tenía que definir un estándar de marco para todas las versiones de .NET Framework, a fin de permitir que el código que se ejecuta en un marco se pueda portar a otro.

Este estándar, por supuesto, se denominó .NET Standard. Su propósito era identificar la API que un marco debía admitir para que las bibliotecas orientadas al estándar pudieran contar con un conjunto específico de API disponibles. Como resultado, al definir el estándar y luego implementarlo con Xamarin/Mono, .NET Core y .NET Framework, este se convirtió en un componente clave que permitía la estrategia de unificación de .NET 5.

Por ejemplo, una vez que todos los marcos han implementado código compatible con el conjunto de API de .NET Standard, parece lógico trabajar para combinar las bases de código separadas en una (una refactorización de ordenaciones). Si el comportamiento no es el mismo (compilación JIT frente a AOT, por ejemplo), ¿por qué no se combina el código para que todas las plataformas admitan ambos enfoques y características? El esfuerzo no es trivial, pero el resultado es un enorme paso hacia adelante para reducir la complejidad y el mantenimiento, al mismo tiempo que se unifican las características para todas las plataformas.

Puede resultar sorprendente que el mismo .NET Standard que hizo posible la unificación hará, probablemente, que .NET Standard carezca de importancia. De hecho, con la aparición de .NET 5, no se sabe si habrá otra versión de .NET Standard (.NET 5 y todas las versiones después de esta serán el estándar).

Resumen

Se dice que el tiempo lo es todo, algo cierto en .NET 5. Una reescritura prácticamente completa de .NET Framework no se podía ni imaginar cuando se inició el desarrollo de .NET Core. Por el momento, Microsoft respondía a la demanda para mejorar significativamente la experiencia de hospedaje de Azure en Linux, en contenedores y en PaaS. Por lo tanto, la compañía estaba centrada en obtener algo para satisfacer las demandas de los clientes y del equipo de producto de Azure.

Con .NET Core 2.0 la misión se expandió para adaptarse a la funcionalidad de .NET Framework. Nuevamente, el equipo se centraba en lanzar algo viable, en lugar de adquirir demasiada responsabilidad. Pero las cosas empezaron a cambiar con .NET Core 3.0 y la implementación de .NET Standard 2.1. La idea de tener que acceder a tres marcos distintos y realizar cambios en estos cuando se presentaba una característica o surgía un error era un engorro y un gasto. Además, como cualquier buen desarrollador, pronto surgió la idea de refactorizar el código tanto como fuese posible en una única base de código.

Así nació .NET 5. Al mismo tiempo, nació la idea de unificar todas las características de cada marco, tanto si se trata de formatos .csproj simples, la adopción de modelos de desarrollo de código abierto, la habilitación de la interoperabilidad con Java y Objective-C (incluido Swift) o la compatibilidad con la compilación JIT y AOT. Del mismo modo, la idea de un marco único y unificado se convirtió en un paso obvio, que espero que todo el mundo, tanto dentro como fuera de Microsoft, celebre.


Mark Michaelis es el fundador de IntelliTect y trabaja de arquitecto técnico como jefe y formador. Durante casi dos décadas, ha sido MVP de Microsoft y director regional de Microsoft desde 2007. Michaelis trabaja con varios equipos de revisión de diseño de software de Microsoft, como C#, Microsoft Azure, SharePoint y Visual Studio ALM. Realiza ponencias en conferencias de desarrolladores y ha escrito varios libros, el más reciente de los cuales es "Essential C# 7.0 (6th Edition)" (itl.tc/EssentialCSharp). Póngase en contacto con él en Facebook en facebook.com/Mark.Michaelis, en su blog IntelliTect.com/Mark, en Twitter @markmichaelis o a través de la dirección de correo electrónico mark@IntelliTect.com.

Gracias al siguiente experto técnico de Microsoft por revisar este artículo: Rich Lander


Comente este artículo en el foro de MSDN Magazine