Veinticuatro años de .NET, versión por versión

Leonardo Gurgitano8 minLeer en inglés

Escalera vista desde abajo, en blanco y negro: niveles sucesivos que se apilan uno sobre otro.
Escalera vista desde abajo, en blanco y negro: niveles sucesivos que se apilan uno sobre otro.

.NET apareció en 2002 funcionando únicamente sobre Windows. Durante una década fue una de las plataformas más usadas para software empresarial, y hacia 2015 tenía un problema serio: no servía para lo que la industria estaba empezando a hacer, que era desplegar aplicaciones en contenedores sobre servidores Linux. La solución no fue adaptar lo que existía. Fue construir una plataforma nueva en paralelo y, con los años, dejar de desarrollar la anterior.

Vale la pena repasarlo versión por versión, porque en cada salto hay una decisión que explica cómo llegamos al .NET de hoy.

La era Framework (2002–2015)

1.0 (febrero de 2002). Microsoft responde a Java con un entorno de ejecución propio, el CLR (el componente que ejecuta el programa y administra su memoria), y un lenguaje nuevo: C#. La propuesta era que varios lenguajes pudieran compilarse al mismo formato intermedio y correr sobre ese entorno. En la práctica el que se usó fue C#.

2.0 (noviembre de 2005). Llegan los genéricos: escribir una vez una lista o un diccionario que después funciona con cualquier tipo, sin perder la verificación del compilador. Java los había incorporado un año antes, pero resueltos solo en compilación: el entorno de ejecución no los conoce, y eso impone limitaciones que Java todavía tiene. .NET los implementó en el entorno de ejecución, con código especializado para cada tipo. Costó más y resultó mejor.

3.0 (2006) y 3.5 (2007). Sobre el mismo entorno aparecen WPF, WCF y, sobre todo, LINQ: consultas escritas dentro del propio lenguaje, con la misma sintaxis para una lista en memoria que para una tabla en la base de datos. Es la característica más imitada de la plataforma; hoy casi todo lenguaje tiene algo equivalente.

4.0 (2010) y 4.5 (2012). La Task Parallel Library, y después async/await: una sintaxis para escribir código que espera resultados —una consulta, una llamada de red— sin bloquear el hilo de ejecución mientras tanto, y sin encadenar funciones de retorno. C# la incorporó antes que casi todos. Hoy la misma sintaxis existe en JavaScript, Python, Rust y Swift.

4.8 (2019). La última versión del Framework clásico. Sigue con soporte porque hay mucho software en producción que depende de ella, pero no va a recibir características nuevas.

Para entonces el problema de fondo era claro. El Framework estaba instalado en el sistema operativo: venía con Windows, se actualizaba con Windows, y dos aplicaciones en el mismo servidor usaban obligatoriamente la misma versión. Cuando la industria se movió a contenedores —imágenes que empaquetan la aplicación con todo lo que necesita para ejecutarse— esa forma de distribución dejó de ser viable.

La ruptura (2016–2019)

.NET Core 1.0 (junio de 2016). Una plataforma nueva escrita desde cero: multiplataforma, de código abierto, y con el entorno de ejecución empaquetado junto a la aplicación en lugar de instalado en la máquina. Eso permite que dos aplicaciones en el mismo servidor usen versiones distintas.

También llegó con una fracción de la biblioteca base. Faltaban funciones muy usadas, buena parte de las bibliotecas del ecosistema no compilaban, y durante un tiempo hubo que elegir entre una plataforma moderna e incompleta y una completa que solo corría en Windows. Muchos equipos se quedaron donde estaban, y con razón.

2.0 (2017) y 2.1 (2018). Acá se recupera. .NET Standard 2.0 define un conjunto común de unas veinte mil funciones que todas las variantes deben implementar, y el ecosistema vuelve a compilar. La 2.1 incorpora Span<T>, un tipo que permite leer y escribir sobre un fragmento de memoria ya existente sin copiarlo. Suena menor y no lo es: evitar copias innecesarias es de donde salen casi todas las mejoras de rendimiento de la plataforma desde entonces.

3.0 y 3.1 (2019). WinForms y WPF pasan a Core, así que las aplicaciones de escritorio también pueden migrar. Y llegan los tipos de referencia anulables: el compilador distingue entre una variable que puede ser null y una que no, y avisa cuando el código no contempla el primer caso. Se activa por proyecto, y reduce mucho una de las fuentes de error más comunes del lenguaje.

La reunificación (2020–2024)

.NET 5 (noviembre de 2020). Desaparece la palabra “Core”. Queda un solo .NET.

Y se saltea el número 4 deliberadamente: ya existía .NET Framework 4.8, y llamar “4” a la versión nueva habría hecho imposible distinguirlas al hablar o al buscar documentación.

Desde acá hay una versión cada noviembre, con un esquema de soporte fijo: las versiones pares son LTS (long-term support, tres años de correcciones de seguridad), las impares tienen soporte corto. Saber de antemano cuándo deja de recibir parches la versión que usás cambia cómo se planifica una migración.

6 (2021). LTS. Las minimal APIs permiten escribir un servicio HTTP en unas veinte líneas, declarando las rutas directamente en lugar de crear clases controladoras.

7 (2022). Native AOT (ahead-of-time): compilar directamente a un ejecutable nativo. Sin esto, .NET traduce el código a instrucciones de máquina la primera vez que se ejecuta cada método, mediante un compilador llamado JIT (just-in-time); eso hace que el arranque sea más lento. Con AOT esa traducción ya está hecha. Importa sobre todo en funciones que se ejecutan bajo demanda y se apagan enseguida, donde ese arranque se suma al tiempo de cada llamada.

8 (2023). LTS. Blazor unifica sus modelos de renderizado: se puede decidir componente por componente qué se ejecuta en el servidor y qué en el navegador, en lugar de elegirlo para toda la aplicación.

9 (2024). Mejoras de rendimiento, y la primera señal de lo que venía: Microsoft.Extensions.AI aparece en versión preliminar.

.NET 10, hoy

Salió el 11 de noviembre de 2025, es LTS y tiene soporte hasta el 10 de noviembre de 2028. Es la versión estable más reciente.

Trae lo esperable y bien hecho: C# 14 con miembros de extensión —ahora se pueden agregar propiedades a un tipo ajeno, no solo métodos— y la palabra clave field para escribir una propiedad con lógica sin declarar a mano la variable que guarda el valor. El compilador JIT mejora la inserción de código y la resolución de llamadas. En procesadores Arm64, las pausas del recolector de basura —los momentos en que el programa se detiene para liberar memoria— bajan entre un 8 y un 20 %, algo que se nota en un servidor con carga. ASP.NET Core suma passkeys, un método de autenticación sin contraseña basado en el dispositivo del usuario, y OpenAPI 3.1 pasa a ser el valor por defecto.

Pero lo que distingue a esta versión es otra cosa.

La inteligencia artificial entra en la biblioteca base

Cuando se habla de integrar inteligencia artificial en una aplicación, hoy se habla casi siempre de modelos de lenguaje: sistemas como los que están detrás de ChatGPT o Claude, que reciben un texto y generan una respuesta. Se usan a través de una API por internet, o ejecutándolos en la propia máquina.

Hasta .NET 9, hacer eso significaba elegir el kit de desarrollo de un proveedor concreto y escribir el código contra él. Pasar de OpenAI a un modelo local implicaba reescribir esa parte.

Microsoft.Extensions.AI llega estable en .NET 10 y define una interfaz común para todos los proveedores, igual que ILogger la define para los sistemas de registro. La principal es IChatClient, y detrás puede haber OpenAI, Azure OpenAI, GitHub Models u Ollama ejecutándose en tu propia máquina.

// El resto del código no sabe qué proveedor hay detrás.
services.AddChatClient(new OllamaChatClient(new Uri("http://localhost:11434")))
        .UseFunctionInvocation()   // el modelo puede llamar a tus métodos
        .UseDistributedCache()     // respuestas repetidas no se piden dos veces
        .UseOpenTelemetry();       // y queda registro de cada llamada

Es la misma composición por capas que ASP.NET Core usa para procesar peticiones: cada Use… envuelve al anterior y agrega una responsabilidad. Que el modelo pueda invocar métodos del programa, que las respuestas se guarden en caché y que todo quede instrumentado no son características del proveedor, sino capas que agrega quien programa.

Alrededor hay dos piezas más. El Microsoft Agent Framework, para coordinar varios de estos componentes trabajando sobre una misma tarea, en secuencia o en paralelo. Y soporte para MCP (Model Context Protocol), un protocolo que estandariza cómo un modelo accede a herramientas externas: consultar una base de datos, leer un archivo, llamar a una API.

EF Core 10, por su parte, incorpora búsqueda vectorial en Azure SQL y Cosmos DB. La técnica consiste en convertir cada texto en una lista de números que representa su significado —lo que se llama un embedding— y buscar los textos cuyas listas están más cerca entre sí, en vez de comparar palabras exactas. Hasta ahora eso requería una base de datos especializada aparte.

Qué tiene de distinto este último capítulo

Los genéricos en 2005, LINQ en 2007, async/await en 2012, Span<T> en 2018: en todos esos casos .NET diseñó algo que después el resto de la industria copió.

Con la inteligencia artificial la posición es la inversa. .NET está adoptando prácticas que se definieron primero en el ecosistema de Python, y llega bastante después. Lo que aporta es aquello en lo que la plataforma tiene experiencia: tomar una práctica que cada equipo resolvía a su manera y convertirla en una interfaz estable, con tipos verificados por el compilador y con registro de lo que ocurre en cada llamada.

Puede que alcance. IChatClient no es una idea original, pero una interfaz común con inyección de dependencias, registro y caché incluidos de fábrica es exactamente lo que le faltaba a esta área para dejar de resolverse con código a medida en cada proyecto.

Qué viene

.NET 11 llega el 10 de noviembre de 2026 y ya está en versiones preliminares. Es una versión de soporte corto, así que si tenés algo en producción y no hay una razón concreta para moverte, .NET 10 es donde conviene quedarse hasta 2028.

De lo que se ve en las vistas previas, dos cosas prometen: los tipos unión en C# 15 —poder declarar que un valor es de un tipo o de otro, y que el compilador obligue a contemplar ambos casos— y el trabajo sobre async a nivel del entorno de ejecución, que apunta a mejorar la depuración de código asincrónico.

Comentarios