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 ;)

20 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!
Mostrando entradas con la etiqueta c#. Mostrar todas las entradas
Mostrando entradas con la etiqueta c#. Mostrar todas las entradas
martes, 9 de junio de 2026
Distintos tipos de vehículos fusionándose

Todos hemos tenido que lidiar con situaciones en las que una función o método puede devolver diferentes tipos de datos dependiendo de ciertas condiciones. El ejemplo clásico es una función que puede devolver un resultado exitoso o un error, lo que a menudo se maneja con tipos como Result<T> o haciendo algunos malabarismos con composiciones de clases o herencia.

var result = GetUser(1);
if(result.Success) 
{
    var user = result.Value;
    // Hacer algo con el usuario
}
else 
{
    var error = result.Error;
    // Manejar el error
}

Son artificios que, aunque funcionan, dan trabajo, complican la legibilidad y ocultan la intención real del desarrollador a la hora de escribir código de procedimientos que manejen múltiples tipos de retorno.

Los tipos unión son una de las características más solicitadas en C# durante años, y finalmente los tendremos disponibles en la versión 15 del lenguaje, que vendrá de la mano de NET 11 en noviembre de 2026.

Con esta nueva capacidad, podremos crear tipos que pueden contener valores de varios tipos diferentes, con controles en tiempo de compilación y sin apenas complejidad adicional.

Lo vemos en detalle a continuación.

martes, 17 de febrero de 2026
Una lambda petrificada

No es algo que se utilice habitualmente, y a veces incluso es un detalle desconocido por los desarrolladores, pero las funciones lambda de C# pueden ser estáticas. Y no es porque sea algo nuevo, pues esta característica se introdujo en la versión 9.0 del lenguaje en 2020... simplemente son esos pequeños detalles que se van añadiendo al lenguaje sin hacer mucho ruido, y que a veces pasan desapercibidos.

De hecho, el uso de lambdas estáticas puede ayudarnos a escribir un código más seguro y predecible, además de tener cierto impacto en el rendimiento de nuestras aplicaciones, por lo que merece la pena conocerlas y saber cuándo y cómo usarlas.

En este post vamos a ver qué son las lambdas estáticas, cómo se implementan y cuándo y por qué deberíamos usarlas.

martes, 18 de noviembre de 2025
C# 14 ya está aquí

Hace unos días se lanzó .NET 10 y, con él, C# 14, una nueva versión del lenguaje que viene con varias novedades interesantes que mejoran la productividad y la experiencia de desarrollo.

Las más destacables son:

Les damos un vistazo rápido a continuación.

martes, 21 de octubre de 2025
Las extensiones de C#, representadas por un edificio que está siendo renovado

Los métodos extensores aparecieron con C# 3.0, hace casi veinte años. Desde entonces, se han convertido en una herramienta muy útil para los desarrolladores de C#, permitiéndonos "añadir" métodos a tipos existentes sin modificar su código fuente ni crear un nuevo tipo derivado. Sin embargo, desde aquella lejana versión, este mecanismo no había sufrido cambios significativos.

Con la llegada de C# 14 y .NET 10, el concepto de extensión se ha hecho mucho más ambicioso. Como veremos a continuación, no solo es posible definir métodos extensores, sino también propiedades y otros tipos de miembros, tanto de instancia como estáticos. Además, la sintaxis se ha mejorado para hacerla más clara y fomentar la cohesión entre los distintos miembros de extensión que implementemos para un mismo tipo de datos.

Lo vemos a continuación.

martes, 1 de julio de 2025
El compilador de C# vestido como un adivino que recibe una carta con el texto MemberNotNullWhen

El compilador de C# es listo, muy listo. Es capaz de analizar en tiempo real nuestro código para comprender lo que estamos haciendo y su contexto y, en muchas ocasiones, advertirnos de posibles errores antes de que seamos conscientes de que están ahí.

Pero lamentablemente aún no es adivino, y en algunas ocasiones tenemos que ayudarlo a que conozca nuestras intenciones. Para estos casos, existen una serie de atributos que añaden metadatos al código y que el compilador puede utilizar para realizar un análisis más preciso.

Hoy vamos a hablar de uno de estos atributos, MemberNotNullWhenAttribute, que nos permite dar pistas al compilador sobre la nulabilidad de un miembro de una clase en función del valor de otro miembro, algo que puede resultarnos muy útil en ciertos escenarios, como veremos a continuación.

martes, 24 de junio de 2025
Desarrolladora de software esperando a que se vacíe un reloj de arena

Estamos acostumbrados a usar el operador await de C# para esperar la finalización de tareas asíncronas representadas por objetos de tipo Task, Task<T>, ValueTask o ValueTask<T>, pero, ¿sabíais que en realidad podemos usarlo con cualquier tipo de objeto?.

En este artículo vamos a ver que es bastante sencillo, y lo ilustraremos con un ejemplo muy simple: cómo esperar un segundo usando la expresión await 1000 o await TimeSpan.FromSeconds(1) en lugar del clásico await Task.Delay(1000).

martes, 27 de mayo de 2025
Desarrolladores estirando .NET

Cuando usamos tipos enumerados en C#, muchas veces buscamos seguridad. Los valores de un enum son constantes conocidas de antemano y se comprueban en tiempo de compilación, lo que evita asignaciones inválidas que podrían derivar en errores en tiempo de ejecución.

También, el hecho de poder acceder a los valores del enum utilizando nombres o identificadores descriptivos da lugar a un código más legible y fácil de mantener. Y encima, las ayudas para el autocompletado y descubrimiento de valores posibles que nos proporcionan los IDEs modernos nos ayudan a ser más productivos. Todo son ventajas 🙂

Sin embargo, los enum de C# sólo permiten que sus valores subyacentes sean numéricos ( byte, sbyte, short, ushort, int -el tipo por defecto-, uint, long o ulong). Esto puede suponer una limitación en casos en los que nos vendría bien disfrutar de todos los beneficios anteriores, pero usando valores string... es decir, deberíamos poder definir un tipo que pueda contener únicamente un conjunto de cadenas de texto predefinidas.

Algunos lenguajes como TypeScript, Python o Swift soportan de forma nativa la creación de enumerados de texto; en cambio, en C# no tenemos esta posibilidad. Pero bueno, no todo está perdido... gracias a la flexibilidad del lenguaje, podemos conseguir algo bastante parecido 😉

martes, 20 de mayo de 2025
Operador con camiseta de C# y pelazo a lo Elvis manipulando una máquina compleja

El operador "?." (también conocido como safe navigation operator, null-conditional operator, optional chaining operator o incluso Elvis operator) se introdujo en C# 6.0, en el año 2015. Este conocido operador simplifica el acceso a miembros de un objeto que ocasionalmente podría ser nulo, evitando la necesidad de realizar comprobaciones explícitas de nulidad.

var friend = GetFriendOrNull(1);
var name = friend?.Name; // Si friend es nulo, name será nulo, pero no explotará

Sin embargo, si queremos acceder a una propiedad para asignarle un valor, el operador "?." no es suficiente. Por ejemplo, si queremos asignar un valor a la propiedad Age de un objeto friend sospechoso de ser nulo, debemos comprobarlo manualmente antes de realizar la asignación:

var friend = GetFriendOrNull(1);
if (friend != null)
{
    friend.Age = 23;
}

Esto cambiará con C# 14, cuyo lanzamiento está previsto para noviembre de 2025, acompañando a .NET 10, que extenderá el uso de este operador también a la asignación de valores a miembros, completando así su funcionalidad.

Se trata de una característica muy esperada por la comunidad desde hace años, ya que simplificará y hará el código más legible. En este artículo, exploraremos cómo funcionará esta nueva característica y qué implicaciones tendrá en nuestro código.

martes, 6 de mayo de 2025
Mago abriendo el capó de un coche para ver la magia que hay dentro

Hace unas semanas estuvimos hablando de los atributos englobados en la categoría de "caller information" de .NET: [CallerFilePath], [CallerLineNumber], [CallerMemberName] y [CallerArgumentExpression]. Como vimos, estos atributos permiten que un método o función conozca información sobre el método que lo ha llamado, como el nombre del archivo, el número de línea, el nombre del método o la expresión de argumento.

Sin embargo, ninguna de estas opciones nos permite saber cuál es la clase que contiene el código que ha llamado al método que está actualmente en ejecución, algo que podría venir bien en algunos escenarios. Por ejemplo, recientemente me lo he encontrado revisando un proyecto en el que se utilizaba el sistema de logging NLog, donde encontraba un código como el siguiente para obtener y almacenar de forma estática un logger para la clase actual:

public static class Program
{
    private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
    public static void DoSomething()
    {
        try
        {
           Logger.Info("Hello world");
           System.Console.ReadKey();
        }
        catch (Exception ex)
        {
           Logger.Error(ex, "Goodbye cruel world");
        }
    }
}  

A la vista de estas líneas, puede parecernos difícil deducir qué hace ese método GetCurrentClassLogger() por detrás para saber cuál es la clase actual y crearle un logger específico para ella.

En este post, vamos a abrir el capó para ver la "magia" que hace esto posible.

martes, 25 de marzo de 2025
Desarrolladores Javascript y C# trabajando juntos

En JavaScript es frecuente encontrar expresiones como esta para comprobar si un objeto tiene valor (o, al menos, si su valor es uno de los reconocidos como truthy):

const friend = getFriend(1); // Obtiene un objeto friend, o null si no existe
if (friend) {
    // Hacer algo con el objeto friend
}

Debido a su sistema de tipos estricto, en C# no es posible hacer lo mismo de forma directa, tenemos que comprobar si el objeto es null, por ejemplo así:

var friend = GetFriend(1); // Obtiene un objeto friend, o null si no existe
if (friend is not null) {
    // Hacer algo con el objeto friend
}

Sin embargo, con muy poco esfuerzo podemos hacer que C# acepte la sintaxis de JavaScript para ese tipo de chequeos de nulidad, implementando en nuestra clase un conversor implícito a bool. Lo vemos a continuación.

martes, 18 de febrero de 2025
Un patito de goma al que se ha forzado a ser un coche

Imaginad que tenemos un objeto clase Coche y otro del tipo PatitoDeGoma, sin ningún tipo de relación entre ellos. C# no permite hacer un cast y convertir libremente de uno a otro tipo o viceversa, ¿verdad?

Pues eso pensaba yo también, hasta que hace poco descubrí una característica de .NET que no he usado nunca y dudo que vaya a usar 😝, pero me ha parecido de lo más curiosa y quería compartirla con vosotros. Como podréis comprobar, es una buena candidata para la serie C# bizarro, de la que ya llevamos varias entregas, aunque en realidad se trata de una característica de la plataforma y no del lenguaje.

La protagonista es la clase estática System.Runtime.CompilerServices.Unsafe, que, según indican en la documentación oficial, implementa funcionalidades de bajo nivel para manipular punteros gestionados y no gestionados. Su objetivo, según esta misma fuente, es permitir a desarrolladores de bibliotecas escribir código de alto rendimiento evitando los chequeos de seguridad de .NET.

Esta clase proporciona métodos de aritmética de punteros, conversión de tipos y otras operaciones que no son seguras en C# y que, por tanto, no se permiten salvo en contextos unsafe. Sin embargo, en ocasiones, puede ser útil para realizar operaciones que no son seguras pero que sabemos que no van a causar problemas en tiempo de ejecución.

El que más me ha llamado la atención ha sido el método As(), que permite forzar un cast entre dos tipos que a priori no son compatibles sin realizar comprobaciones de tipo en tiempo de ejecución.

martes, 17 de diciembre de 2024
Programador rupestre

A principios de siglo, con un C# aún bastante primitivo, las propiedades que almacenaban datos en una clase teníamos que definirlas siempre usando un backing field, es decir, un campo privado que almacenaba el valor, y accediendo a él mediante métodos getter y setter. Es decir, algo así:

public class Customer
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
    ...
}

Esto cambió en 2007 con la llegada de las propiedades automáticas, una maravilla para nuestros cansados dedos, que desde entonces usamos en la mayoría de ocasiones. Gracias a ellas, podemos definir una propiedad sin tener que implementar sus métodos accesores ni su campo privado correspondiente, porque el compilador se encargaba de crearlos por nosotros. De esta forma, código anterior quedaba así:

public class Customer
{
    public string Name { get; set; }
    ...
}

En realidad, se trataba sólo de un azucarillo sintáctico. Por detrás, el compilador está generando el campo privado y transformando nuestro código para utilizarlo; de hecho, el código generado de la clase anterior es algo así (algo simplificado para que se pueda leer mejor):

public class Customer
{
    private string <Name>k__BackingField;

    public string Name
    {
        get { return <Name>k__BackingField; }
        set { <Name>k__BackingField = value; }
    }
}

Aunque obviamente usar propiedades automáticas es mucho más cómodo que la opción anterior, tiene un inconveniente: no tenemos forma de acceder al campo generado por el compilador, algo que puede resultar interesante si nos interesa realizar algún tipo de validación o lógica adicional en el getter o setter más allá de simplemente devolver o asignar el valor.

Por ejemplo, imaginemos que queremos que el nombre de nuestro cliente siempre esté en mayúsculas. Con una propiedad automática, simplemente no podemos hacerlo, y tendremos que recurrir a las verbosas propiedades tradicionales con backing fields.

Y aquí es donde entran en escena las propiedades semiautomáticas...

martes, 29 de octubre de 2024
Piezas de un puzzle sugiriendo que todo va encajando en .NET

Seguimos analizando las novedades de .NET 9, y en esta ocasión le toca el turno a la actualización en miembros de clases parciales de C# 13.

Las clases o tipos parciales de C# están con nosotros casi desde los inicios de .NET, desde los tiempos de C# 2.0. Como sabemos, éstas permiten dividir la implementación de un tipo en varios archivos. Su existencia se debía a varios motivos:

  • Permitir a los desarrolladores trabajar en clases de forma concurrente sin bloquearse (recordemos que por aquellos tiempos aún pululaban por ahí infames sistemas de control de código fuente como SourceSafe).
  • Trocear clases extensas para facilitar su comprensión y mantenimiento.
  • Y, el que es más importante, posibilitar el uso sencillo de herramientas de generación de código que, de alguna forma, tuvieran que completar la implementación de código del usuario.

Acompañando a las clases parciales, se introdujo también el soporte para métodos parciales. Aunque su uso era limitado y podía depararnos algunas sorpresas, eran útiles para comunicar de forma segura distintos fragmentos de la clase. Básicamente, consistía en que una porción de una clase podía definir un método e invocarlo desde su código, delegando a otras porciones su implementación. Si no se implementaba en ninguna otra parte, simplemente no se generaba el código de la invocación.

Veamos un ejemplo del uso de estas características:

// Archivo: Ejemplo.Main.cs
public partial class Ejemplo
{    
    // El método parcial se declara sin implementación...
    partial void Log(string msg);

    public void RealizarAlgo()
    {
        hacerAlgoComplejo();
        Log("¡Ojo!"); // Usamos el método
                    // parcial declarado antes
    }
}

// Archivo: Ejemplo.Log.cs
public partial class Ejemplo
{
    partial void Log(string msg)
    {
        Console.WriteLine(msg);
    }
}

Pues bien, casi dos décadas más tarde, los tipos parciales van a recibir algunas actualizaciones interesantes en C# 13, posibilitando que, además de métodos, puedan definirse propiedades parciales.

martes, 15 de octubre de 2024
.NET 9 apareciendo en el horizonte

Cada vez que llega una nueva versión del framework, todos estamos ansiosos por conocer las novedades, especialmente las más espectaculares. Sin embargo, a veces hay pequeñas mejoras que, sin ser revolucionarias, nos pueden resultar útiles en algunos escenarios.

El modificador params permite especificar que un método o función acepta un número variable de argumentos, como en el siguiente ejemplo, donde vemos una función Sum() que puede ser invocada con cualquier número de valores:

// Declaración de la función:
int Sum(params int[] values) 
{
    return values.Sum();
}

// Ejemplos de uso:
Console.WriteLine(Sum(1)); // Enviamos un único valor -> "1"
Console.WriteLine(Sum(1, 2, 3, 4, 5)); // Enviamos cinco valores -> "15"
Console.WriteLine(Sum()); // No enviamos elementos, se recibirá un array vacío -> "0"
Console.WriteLine(Sum([1, 2, 3])); // Aquí pasamos un array directamente -> "6"

Hasta ahora, el parámetro param sólo podía ser un array, lo que limitaba un poco su uso. Por ejemplo, no podríamos llamar a la función Sum() pasándole directamente un List<int> o un IEnumerable<int> sin convertirlos antes a array porque fallarían en tiempo de compilación, al no tratarse de un int[], que es el tipo del parámetro esperado.

¿Qué ha cambiado?

martes, 2 de julio de 2024
C#

Cuando en un bucle for necesitamos más de una variable de control, lo habitual es inicializarlas fuera del bucle y luego usarlas en su interior, por ejemplo, como en el siguiente código:

int j = 0;
for (int i = 0; i < 10 && j < 100; i++)
{
    Console.WriteLine($"i = {i}, j = {j}");
    j+= 10;
}
martes, 18 de junio de 2024
C#

Llevamos ya varias entregas de la serie C# bizarro, donde, como sabéis, ponemos a prueba nuestros conocimientos de C# llevando al extremo el uso de algunas de sus características, lo que de paso nos ayuda a conocer mejor aún el lenguaje.

Hoy vamos a proponer un nuevo reto, mostrando un comportamiento que quizás muchos conozcáis, pero seguro que otros nunca habéis tenido ocasión de verlo. A ver qué tal se os da ;)

Observad atentamente esta sencilla aplicación de consola:

int i = 1;
int accum = 0;
while (i <= 5)
{
    accum += i;
    Debug.WriteLine($"Adding {i++}, Accumulated = {accum}");
}
Console.WriteLine($"Total: {accum}");

A la vista del código, las preguntas son dos: ¿Qué resultado obtenemos al ejecutar la aplicación? ¿Será siempre así?

Obviamente, tiene algo de truco, pero si lo pensáis un poco seguro que podéis responder a las preguntas. Y si no, pulsad aquí para ver la solución; 👇👇

En principio, lo lógico es pensar que deberíamos ver en el canal de depuración (la ventana Output>Debug en Visual Studio)  el valor actual y el total acumulado en cada iteración, lo que nos permite ir siguiendo en detalle el funcionamiento interno:

Adding 1, Accumulated = 1
Adding 2, Accumulated = 3
Adding 3, Accumulated = 6
Adding 4, Accumulated = 10
Adding 5, Accumulated = 15

Finalmente, se mostrará por consola el valor definitivo:

Total: 15

Esto será justamente lo que obtengamos si copiamos el código en Visual Studio y lo ejecutamos pulsando F5 o el botón correspondiente del IDE. Hasta ahí, perfecto ;)

Sin embargo, este código tiene una trampa oculta. Si desde Visual Studio cambiamos el modo de compilación a "Release" y lo lanzamos, o bien si lanzamos directamente el ejecutable que encontraremos en la carpeta bin/Release/net8.0 (o la versión de .NET que uses, da igual), veremos que la aplicación no se detiene nunca (?!)

El motivo de este extraño comportamiento lo explicamos hace ya bastantes años por aquí, en el post métodos condicionales en C#.

Estos métodos, presentes desde las primeras versiones de .NET (pre-Core), se decoran con el atributo [Conditional] para hacer que éstos y todas las referencias a los mismos sean eliminadas del ensamblado resultante de la compilación si no existe una constante de compilación determinada.

De hecho, si acudimos al código fuente de la clase estática Debug, veremos que su método WriteLine() está definido de la siguiente manera:

public static partial class Debug
{
    ...
    [Conditional("DEBUG")]
    public static void WriteLine(string? message) 
        => s_provider.WriteLine(message);
}

Cuando compilamos en modo depuración, la constante DEBUG estará definida, por lo que este método podrá ser invocado con normalidad y todo funcionará bien. Sin embargo, si compilamos en Release o cualquier otra configuración que no incluya la constante, este método desaparecerá del ensamblado, junto con las referencias que lo utilicen.

Es decir, si usamos el modo Release, el código que hemos escrito antes quedará tras la compilación como el siguiente:

int i = 1;
int accum = 0;
while (i <= 5)
{
    accum += i;
    // La siguiente llamada será eliminada en compilación:
    // Debug.WriteLine($"Adding {i++}, Accumulated = {accum}");
}
Console.WriteLine($"Total: {accum}");

Fijaos que, al eliminar la llamada, con ella desaparecerá también la expresión de autoincremento del índice del bucle i++, por lo que nunca se alcanzará la condición de salida y quedará iterando de forma indefinida.

Bonito, ¿eh? ;)

Publicado en Variable not found.

martes, 28 de mayo de 2024
C#

Me da la impresión de que el operador with de C# no es demasiado conocido, y creo que vale la pena echarle un vistazo porque puede resultarnos útil en nuestro día a día.

Introducido con C# 9, allá por 2020, las expresiones with proporcionan una forma sencilla de crear nuevas instancias de objetos "copiando" las propiedades de otro objeto, pero con la posibilidad de modificar algunas de ellas sobre la marcha. Esto, aunque puede usarse con varios tipos de objeto, es especialmente interesante cuando estamos trabajando con componentes inmutables, como son los records, porque la única posibilidad que tenemos de alterarlos es creando copias con las modificaciones que necesitemos.

martes, 21 de mayo de 2024
C#

Revisando código ajeno, me he encontrado con un ejemplo de uso del operador null coalescing assignment de C# que me ha parecido muy elegante y quería compartirlo con vosotros.

Como recordaréis, este operador, introducido en C# 8, es una fórmula muy concisa para asignar un valor a una variable si ésta previamente es null, una mezcla entre el operador de asignación y el null coalescing operator que disfrutamos desde C# 2:

// C# "clásico":
var x = GetSomething();
...
if(x == null) {
    x = GetSomethingElse();
}

// Usando null coalescing operator (C# 2)
var x = GetSomething();
...
x = x ?? GetSomethingElse();

// Usando null coalescing assignment (C# 8)
var x = GetSomething();
...
x ??= GetSomethingElse();

Pero, además, al igual que otras asignaciones, este operador retorna el valor asignado, lo que nos permite encadenar asignaciones y realizar operaciones adicionales en la misma línea.

Por ejemplo, observad el siguiente código:

Console.WriteLine(y ??= "Hello, World!");

Aquí, básicamente lo que hacemos es asignar el valor "Hello, World!" a la variable si ésta contiene un nulo, y luego imprimirlo por consola. Y si no es nulo, simplemente se imprime su valor actual.

martes, 23 de abril de 2024
C#

Cuando tenemos una colección de datos para procesar, es relativamente habitual tener que hacerlo por lotes, es decir, dividir la colección en partes más pequeñas e irlas procesando por separado.

Para ello, normalmente nos vemos obligados a trocear los datos en lotes o chunks de un tamaño determinado, y luego procesar cada uno de ellos.

Por ejemplo, imaginad que tenemos un método que recibe una lista de objetos a procesar y, por temas de rendimiento o lo que sea, sólo puede hacerlo de tres en tres. Si la lista de elementos a procesar es más larga de la cuenta, nos veremos previamente obligados a trocearla en lotes de tres para ir procesando cada uno de ellos por separado.

Tradicionalmente, es algo que hemos hecho de forma manual con un sencillo bucle for que recorre la lista completa, y va acumulando los elementos en un nuevo lote hasta que alcanza el tamaño deseado, momento en el que lo procesa y lo vacía para empezar de nuevo.

Pero otra posibilidad bastante práctica, y probablemente más legible, sería pasar la lista de elementos previamente por otro proceso que retorne una lista de chunks, o pequeñas porciones de la lista original, para que luego simplemente tengamos que ir procesándolos secuencialmente. Es decir, algo así:

private char[] array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
private int chunkSize = 3;

var chunks = GetChunks(array, chunkSize); // [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'], ['j']
foreach (var chunk in chunks) { ProcessChunk(chunk); // 'chunk' sería un array de 3 elementos }

Y la pregunta es, ¿cómo podríamos implementar ese método GetChunks() en C#? Vamos a ver varias opciones.

miércoles, 3 de abril de 2024
C#

Hoy va un post rápido sobre una característica de C# 12 que, aunque pequeña, nos puede resultar interesante en determinadas situaciones y puede pasar fácilmente desapercibida entre todas las demás novedades de esta versión del lenguaje.

Hasta C# 11 (.NET 7), no teníamos forma de definir una expresión lambda con parámetros opcionales y valores por defecto, como podemos hacer en los métodos o funciones normales, lo que complicaba la implementación de algunos escenarios.