c# - operator - Tome el mayor de dos valores nulables
string null c# (10)
Supongamos que tengo dos enteros anulables:
int? a = 10;
int? b = 20;
Quiero tomar el valor más grande, que no sea nulo, de modo que si ambos valores son nulos, el resultado es nulo.
Podría escribir algo tan largo como:
int? max;
if (a == null)
{
max = b;
}
else if (b == null)
{
max = a;
}
else
{
max = a > b ? a : b;
}
Esto se siente un poco demasiado torpe (y posiblemente propenso a errores) para mi gusto. ¿Cuál es la forma más sencilla de devolver el mayor valor, lo que también explica la posibilidad de valores nulos?
¿Qué hay de hacer un método capaz de manejar tantos valores anulables como tienes?
public static int? GetLargestNonNull(params int?[] values)
{
IEnumerable<int?> nonNullValues = values.Where(v => v.HasValue);
if (nonNullValues.Any())
{
return nonNullValues.Select(v => v.Value).Max();
}
return null;
}
Y usar como:
int? result = GetLargestNonNull(a, b);
Excepto que esto también es capaz de manejar:
int? result = GetLargestNonNull(a, b, c, d, e, f);
O puede cambiar el parámetro del método para aceptar una Lista si está trabajando con valores que ha recibido de otra fuente.
Aquí hay una solución muy intuitiva y legible. Esto funcionará para cualquier número de valores, así como cualquier estructura anulable como, por ejemplo, int? o DateTime?
Además, si todos los valores son nulos, devuelve nulo.
public static T? GetGreatestOrDefault<T>(IEnumerable<T?> values) where T : struct
{
var any = values.Any(a => a.HasValue);
if (!any) return null;
var firstOrDefault = values.Where(v => v.HasValue)
.Select(v => v.Value)
.OrderByDescending(o => o)
.FirstOrDefault();
return firstOrDefault;
}
o quizás quieras hacer lo siguiente:
public static T? GreatestOrDefault<T>(this IEnumerable<T?> values) where T : struct
{
var any = values.Any(a => a.HasValue);
if (!any) return null;
var firstOrDefault = values.Where(v => v.HasValue).Max();
return firstOrDefault;
}
En una línea usando el operador de unión nula:
int? c = a > b ? a : b ?? a;
Estas líneas muestran la lógica necesaria con un pequeño truco:
if (a == null) return b; // handles b== null also
if (b == null) return a;
// now a!=null, b!=null
return Math.Max(a.Value, b.Value);
o en una línea usando ?:
(exactamente la misma lógica)
return a == null ? b : b == null ? a : Math.Max(a.Value, b.Value);
Editar
Si bien la respuesta anterior es interesante para fines educativos, no es la forma recomendada de resolver este problema. La forma recomendada es no reinventar la rueda en lugar de encontrar la rueda correspondiente:
Como @roman señaló, existe un método Nullable.Compare()
que lo hace mucho más legible:
return Nullable.Compare(a, b) > 0 ? a : b;
Este es un buen lugar para el operador de fusión Null ??
.
Devuelve el primer valor si el valor no es null
, de lo contrario, devuelve el segundo valor.
int? c = a > b ? a : b ?? a;
Usando el hecho de que el operador de comparación devolverá false
si cualquiera de los valores es null
, la expresión le dará el resultado deseado:
a | b || a>b | a | b??a | a>b ? a : b??a
--------------------||----------------------------------
> b | NOT NULL || T | a | -- | a
≤ b | NOT NULL || F | -- | b | b
NOT NULL | NULL || F | -- | a | a
NULL | NOT NULL || F | -- | b | b
NULL | NULL || F | -- | NULL | NULL
Esto funciona para cualquier nullable:
Nullable.Compare(a, b) > 0? a: b;
Me gustaría añadir que las soluciones de una línea aquí son buenas. Pero para desmitificar el código, agregue un poco de corchetes alrededor del operador de unión nula
private int? Max(int? a, int? b)
{
return a > b ? a : (b ?? a);
//returns a if bigger else non null prefering b
}
Devuelve a
si es más grande, si no devuelve b ?? a
b ?? a
- devuelve un valor no nulo (o nulo si ambos son nulos) prefiriendo b
Qué tal esto
private int? Greater(int? a, int? b)
{
if(a.HasValue && b.HasValue)
return a > b ? a : b;
if(a.HasValue)
return a;
if(b.HasValue)
return b;
return null;
}
o más conciso:
private int? Greater(int? a, int? b)
{
if(a.HasValue && b.HasValue)
return a > b ? a : b;
return a.HasValue ? a : b;
}
Una versión corta es:
var result = new[] { a, b }.Max();
!b.HasValue || a > b ? a : b
Si b
es nulo ( !b.HasValue
) entonces a
siempre será la respuesta correcta.
Si b
no es nulo pero a
es, entonces a > b
será falso, y b
será la respuesta correcta.
De lo contrario es lo mismo a > b ? a : b
a > b ? a : b
que tendrían los enteros no anulables.