
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.
Cómo crear un conversor implícito a bool
Nuestro objetivo es hacer posible que las siguientes líneas de código sean válidas en C#, es decir, que compilen y funcionen sin errores:
var friend = GetFriend(1); // Obtiene un objeto friend, o null si no existe
if (friend)
{
// Hacer algo con el objeto friend
}
Dado que en la instrucción if
se espera un predicado, es decir, una expresión de que se evalúe a true
o false
, necesitamos que el objeto friend
se pueda convertir a bool
de forma directa. Para estos escenarios, C# nos ofrece la posibilidad de crear conversores implícitos, es decir, métodos que permiten convertir un objeto de una clase a otro tipo de forma automática, sin necesidad de hacer castings o introducir verbosas conversiones explícitas.
Por tanto, si queremos que if(friend)
sea una expresión válida en C#, tendremos que crear un método de este tipo a la clase del objeto friend
, algo que simplemente consiste en añadir un método public static implicit operator bool
a ésta.
Por ejemplo, si tenemos una clase Friend
que queremos que se pueda convertir directamente a bool
, podemos hacerlo así:
public class Friend
{
public string Name { get; set; }
... // Otras propiedades y métodos de Friend
// Añadir esta línea 👇
public static implicit operator bool(Friend friend) => friend is not null;
}
De esta forma tan sencilla, estamos haciendo que la clase Friend
se pueda convertir a bool
de forma implícita, sin necesidad de hacer castings o conversiones explícitas. Por tanto, podremos utilizar directamente un objeto de tipo Friend
en cualquier sitio donde se espere un bool
, como en la condición de un if
:
// Comparaciones
if (friend) { /* Hacer algo con el objeto friend */ }
if(friend == true) { /* Hacer algo si friend no es null */ }
if (!friend) { /* Hacer algo si friend es null */ }
if(friend == false) { /* Hacer algo si friend es null */ }
bool isFriend = friend; // isFriend será true si friend no es null, y false si lo es
var query = GetFriends().Where(f => f); // Filtra los amigos que no son null
Obviamente, en la condición del conversor implícito podríamos añadir cualquier otra lógica, por ejemplo, podríamos especificar que el objeto Friend
tiene que tener un nombre para que se considere verdadero:
public static implicit operator bool(Friend friend)
=> friend is not null && !string.IsNullOrEmpty(friend.Name);
Pero ojo, úsese con precaución
Aunque a primera vista implementar un conversor de este tipo tiene sus ventajas (indudablemente el código queda más conciso, y a priori, más legible), hay que tener en cuenta que en algunos casos se complicará su comprensión, y puede llevar a errores difíciles de depurar.
Por ejemplo, si otra persona del equipo tiene que mantener el código, puede que no sea evidente a simple vista qué significa if(friend)
. Los operadores implícitos no es algo que todos los programadores de C# conocen, por lo que puede que no sepan que esa conversión se está haciendo de forma automática y tendrán que ir a mirar en la clase Friend
para entenderlo.
En otros casos, puede que esta conversión implícita no sea evidente. Como muestra, en el siguiente código, ¿qué significa el friend
que estamos enviando al método? ¿pasamos el objeto completo, o simplemente su valor booleano? No podemos saberlo sin mirar la implementación o la firma del método doSomething()
:
// Qué es 'friend'? ¿El objeto completo, o su valor booleano?
doSomething(args, friend);
// En estos casos es mejor usar expresiones cuyo sentido
// sea más evidente, aunque sean más verbosas:
doSomething(foo, friend is null);
doSomething(foo, friend.IsValid());
En definitiva, como en todo, hay que encontrar un equilibrio entre la concisión y la claridad del código, y, si no lo vemos claro, no abusar de las posibilidades de extensión que nos ofrece el lenguaje.
Publicado en Variable not found.
2 Comentarios:
¡Sobrecarga de operadores! Enga, que solo le falta la herencia múltiple y le podemos rotar 45 grados el #, je je.
Dale tiempo... 😉
Enviar un nuevo comentario