Lo cierto es que no suelo utilizarlas mucho (por no decir rara y extrañísima vez), pero como siempre estamos buscando una oportunidad para poner en juego características del lenguaje, recientemente me he encontrado con un caso que he resuelto con una conversión de usuario.
Para poner en situación, primero veremos un ejemplo de cómo son y qué hacen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | using System; namespace ConsoleApplication1 { class Program { static void Main( string [] args) { var age = 38; Person person = age; // implícita de int a Person Console.WriteLine(person.Age); // 38 age = ( int )person; // explícita, necesario cast, de Person a int Console.WriteLine(age); // 38 Console.ReadKey(); } } public class Person { public string Name { get ; set ; } public int Age { get ; set ; } public static implicit operator Person( int age) { return new Person() { Age = age }; } public static explicit operator int (Person person) { return person.Age; } } } |
1 2 3 4 5 | public static { implicit | explicit } operator TipoDestino (TipoOrigen identificador) { // do something… return TipoDestino; } |
- Sólo son válidas para clases y estructuras.
- TipoDestino y TipoOrigen tienen que ser de distinto tipo.
- No puede haber una relación de herencia entre TipoDestino y TipoOrigen
- TipoDestino y TipoOrigen no pueden ser de un tipo interface ni object.
- No se puede declarar la misma conversión tanto de forma implícita como explícita
- No se puede usar el operador is
- No se puede usar el operador as
Después de la teoría y ya sabiendo que es una conversión de usuario, el ejemplo donde he aplicado una conversión de usuario es en un método que devuelve si se puede o no enviar un pedido. El problema está en que si no se puede enviar el pedido, querría saber también el motivo. Es decir, el siguiente código no me servía.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class Order { public string CustomerId { get ; set ; } public DateTime? DeliveryDate { get ; set ; } public bool CanBeSent() { if ( string .IsNullOrEmpty(CustomerId)) { return false ; } if (DeliveryDate == null ) { return false ; } return true ; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | using System; namespace ConsoleApplication1 { class Program { static void Main( string [] args) { var order = new Order(); var result = order.CanBeSent(); if (result) { Console.WriteLine( "Se puede enviar el pedido" ); } else { Console.WriteLine( "No se puede enviar el pedido" ); Console.WriteLine(result.Reason); } Console.ReadKey(); } } public class CanBeResult { public bool Value { get ; set ; } public string Reason { get ; set ; } public CanBeResult( bool value) { Value = value; } public CanBeResult( bool value, string reason) : this (value) { Reason = reason; } public static implicit operator bool (CanBeResult result) { return result.Value; } public static implicit operator CanBeResult( bool value) { return new CanBeResult(value); } } public class Order { public string CustomerId { get ; set ; } public DateTime? DeliveryDate { get ; set ; } public CanBeResult CanBeSent() { if ( string .IsNullOrEmpty(CustomerId)) { return new CanBeResult( false , "El pedido no tiene cliente" ); } if (DeliveryDate == null ) { return new CanBeResult( false , "El pedido no tiene fecha de entrega" ); } return true ; } } } |
Hola Sergio,
ResponderEliminarMuy buena explicación. Un caso en que suelo utilizar las conversiones (además implícitas) es cuando creo builders para tests (https://gist.github.com/jmhdez/8332445).
Así puedo escribir código como:
order.AddLine(Build.Product("Camiseta").Size("L").Color("Rojo"), 2);
De esta forma el fluent interface del ProductBuilder, que devuelve en cada paso una referencia a un ProductBuilder, se puede convertir en cualquier momento de forma implícita a un objeto Product para pasarlo como parámetro de un método.
Un saludo,
Juanma.
Gracias Juanma,
EliminarDe hecho tú fuiste el germen de este post :)
Las conversiones era lo típico que había leído pero nunca había visto usarlas, después entre tu post (http://blog.koalite.com/2014/01/aplicando-el-patron-builder-para-escribir-tests-unitarios/) y el uso de librería netfx (patrón especificación para LINQ https://www.nuget.org/packages/netfx-Patterns.LinqSpecs/) que hace un uso intensivo de estas conversiones me picó el gusanillo :)
Un saludo.