Saltar al contenido

Blog Tecnológico | Removiendo el Nulo de C#

Las referencias nulas pueden ser una fuente de sutiles errores en el software. Tal vez es una herramienta que, aunque engañosamente similar, proporciona una seguridad mucho mayor. Veamos un ejemplo de recuperación de un documento de una base de datos. Primero, con código que usa nulos.

public classAccountRepository{publicAccountFind(stringid){/* ... */}}// ...varaccount=accountRepository.Find("nombre de usuario");Console.WriteLine(account.FirstName);

Llamando al método de búsqueda en el Depósito de Cuentas, obtenemos una referencia a una cuenta. Imprimir el primer nombre funciona, siempre y cuando una cuenta exista realmente con el ID dado. De lo contrario, obtendremos la temida NullReferenceException. Para evitar esto tenemos que añadir una comprobación nula.

Blog Tecnológico | Removiendo el Nulo de C#
Blog Tecnológico | Removiendo el Nulo de C#
varaccount=accountRepository.Find("nombre de usuario");if(account!=null){Console.WriteLine(account.FirstName);}

Es muy fácil olvidar ese cheque nulo, o convencerse de que, en este escenario en particular, nunca podría suceder. Tal vez hace imposible ignorar el caso de «sin valor» envolviendo el valor. Veamos cómo funciona el Maybe (también conocido como opción) en F#.

letfindAccount(id:string):Accountoption=if/*wehaveanaccount*/thenSomeaccountelseNoneletaccountOption=findAccount "nombredeusuario "matchaccountOptionwith|Someaccount-;Console.WriteLine(account.FirstName)|None-{{{console.WriteLine("Cuenta no encontrada")

En F#, es imposible que se asigne un tipo de opción nula. El sistema de tipos simplemente no lo permite. Para dar un valor a la opción usamos el constructor Some. De lo contrario, usamos None. Entonces podemos hacer coincidir el valor con el patrón para ver si tenemos un Some o un None. A continuación, veamos una simple implementación de Tal vez en C#. La base de este código es del curso de encapsulación y solidez de Mark Seeman en .

publicstructMaybe<T>{readonlyIEnumerable<T[;valores;publicstaticMaybe<T[;Algunos(Tvalue){if(value==null){thrownewInvalidOperationException()]; }retornonuevoTal vez<(nuevo[]{valor});}públicoestáticoTal vez<T&;Ninguno==;nuevoTal vez<(nuevoT[0]);privadoTal vez(IEnumerables<valores){esto. valores=valores;}publicboolHasValue={valores!=null&&valores.Any();publicTValue{get{if(!HasValue){thrownewInvalidOperationException("Tal vez no tenga un valor");}returnvalues.Single();}}

Con Maybe, hemos envuelto un conjunto en un envoltorio de objetos. Hay un significado en el uso de una matriz, en lugar de un baremo y una bandera que indica dónde hay un valor. Maybe es en realidad un caso especial de tipos de listas (como IEnumerablein .Net). En lugar del tipo que contiene cero a muchos elementos, contiene cero o uno. Reescribamos nuestro ejemplo anterior usando Maybe.

public classAccountRepository{publicMaybe<Account};Find(stringid){si(/* la cuenta fue encontrada en la base de datos */){returnMaybe<Account};.Some(account);}else{returnMaybe<Account Ninguna;}}// ...varaccount=accountRepository.Find("nombre de usuario");if(account.HasValue){Console.WriteLine(account.Value.FirstName);}else{Console.WriteLine("No se encontró ninguna cuenta");}

Trabajar con el tipo Maybe tal como está no proporciona mucha ventaja sobre las referencias nulas, ya que todavía tenemos que hacer una comprobación del valor con HasValue. Añadamos un método para hacer el trabajo con él más natural, similar a la expresión de coincidencia en F#.

publicUCase<T,U>(Func<T,U>algunos,Func<U>ninguno){devuelveesto.TieneValor?algunos(este.Valor):ninguno();}

Ahora, usar «Tal vez» se siente similar a trabajar con una expresión de partido.

varmaybeAccount=accountRepository.Find("nombre de usuario");varmessage=maybeAccount.Case(some:account=§;account.FirstName,none:()=> "No se encontró ninguna cuenta");Console.WriteLine(message);

Moverse en esta dirección hace que el código sea más expresivo, centrándose más en lo que se debe producir, en lugar de buscar los casos de esquina. Pero, al mismo tiempo, forzamos al programador a considerar el caso None, ya que es imposible llamar a Case sin una función none.

También podemos usar «Tal vez» para codificar en un estilo funcional. Veamos una implementación del mapa.

<pre>publicTal vez<U>Map<T,U>(Func<T,U>mapper){regresa esto. ¿Tiene Valor? Tal vez<U>.Algunos(mapper(este.Valor));:Tal vez<U>.Ninguno;}</pre>

Ahora podemos continuar el cómputo de un resultado, independientemente de si un valor está presente.

<pre>public Tal vez<int> Dividir (int a, int b){ return b == 0 ? Maybe<int>.None : Maybe<int>.Some(a / b);}public Maybe<int> DoAComputation(int a, int b){ return Divide(a, b) . Mapa(x => x * x) .Mapa(x => x + 1);}var x = DoAComputation(4, 2); // => Some(5)var y = DoAComputation(5, 0); // => None</pre>

Aunque trabajar con Maybe puede no parecer tan natural en C# como en un idioma con soporte de primera clase, puede reducir enormemente el número de excepciones de referencia nulas. También guía el código en una dirección más funcional. El uso de Maybe en el código de C# proporciona un cambio de paradigma similar para las referencias nulas que LINQ hizo para los tipos de listas. Pruebe la implementación en https://github.com//maybe-dotnet.

Categorías: technicalTags: c-sharp, functional programming, getting started