C# 9.0 – Specification – Target-Typed Conditional Expressions
Índice general – C# 9.0 – Specification
Introducción
Imaginemos la situación de evaluación (condition ? expression1, expression2).
Dentro de la especificación de C# 9 se ha agregado una característica que es realmente útil cuando en una expresión condicional como la que represento anteriormente, no hay un tipo común entre dos expresiones a la hora de ser evaluadas, o bien, en el caso de que haya un tipo común para una de las expresiones, pero no haya una conversión implícita para el tipo.
El objetivo de esta nueva característica agregada a la especificación de C# es la crear una mejor conversión desde las expresiones.
Expresiones condicionales en C# antes de C# 9
Como siempre, partiremos de un código que funciona en C# 8 y otras especificaciones del lenguaje anteriores.
public class Program { public static void Main(string[] args) { var isOdd = true; MyFunction(isOdd ? 1 : 2); } private static void MyFunction(long number) => Console.WriteLine($"{number.GetType()}: {number}"); }
En este código, MyFunction es ejecutada pasándole como parámetro 1 ó 2 (en este caso 1).
Ahora bien, si modificamos MyFunction por esta otra función:
private static void MyFunction(short number) => Console.WriteLine($"{number.GetType()}: {number}");
Nos encontraremos con un error de codificación CS1503 (Argument 1: cannot convert from ‘int’ to ‘short’).
La expresión no es capaz de solventar el problema que planteo, y en realidad y desde un punto de vista general, el código sí debería ser válido.
Para resolver el problema, debemos escribir nuestro código similar a:
public class Program { public static void Main(string[] args) { var isOdd = true; MyFunction(isOdd ? (short)1 : (short)2); } private static void MyFunction(short number) => Console.WriteLine($"{number.GetType()}: {number}"); }
Expresiones condicionales en C# 9
En la especificación de C# 9, este problema queda solventado.
Así que el siguiente código es completamente válido y se resolverá correctamente.
public class Program { public static void Main(string[] args) { var isOdd = true; MyFunction(isOdd ? 1 : 2); } private static void MyFunction(short number) => Console.WriteLine($"{number.GetType()}: {number}"); }
De igual modo, si nuestra función MyFunction es de tipo long, también quedará resuelto correctamente.
Sólo existirá aquí, un problema, y es que si tenemos el siguiente código:
using System; public class MyClass { static void Main(string[] args) { var isOdd = true; MyFunction(isOdd ? 1 : 2); } static void MyFunction(short number) => Console.WriteLine($"{number.GetType()}: {number}"); static void MyFunction(long number) => Console.WriteLine($"{number.GetType()}: {number}"); }
Este código llamará por defecto a la función que tiene como parámetro un long.
No dará ningún error como pasaba en C# 8 o anteriores, pero no resolverá automáticamente el tipo. Para ello, una vez más, deberíamos indicar el tipo de forma implícita.
Por ejemplo:
... MyFunction(isOdd ? (short)1 : (short)2); ...
Y deberemos indicar el tipo en cada parámetro, porque si hacemos esto:
... MyFunction(isOdd ? (short)1 : 2); ...
Seguirá resolviéndolo como long.
Otra cosa es que indiquemos implícitamente el tipo:
... short one = 1; short two = 2; MyFunction(isOdd ? one : two); ...
En este caso, se resolverá de acuerdo al método MyFunction(short number).
Es decir, de acuerdo a todos los ejemplos que he mostrado anteriormente, el lenguaje utiliza la mejor conversión posible de acuerdo a las expresiones. Por ejemplo, si ponemos (short, int), un short no cabe en un int, y por lo tanto usa int. Si por otro lado ponemos (short, short) o bien (int, int), no hay duda alguna.
Para finalizar, recordar que podemos combinar el uso de expresiones con expresiones switch (ver Pattern Matching Enhancements en C# 9 para más información), como por ejemplo:
... MyFunction(isOdd switch { true => 1, false => 2 }); ...
Happy Coding!