Pregunta

Tengo una función, que puede devolver diferentes tipos, y uso sindicato discriminado para esto. Lo que necesito es tener la conversión de un tipo en unión discriminada a otro tipo. Además, algunos de los tipos pueden ser convertibles en todos los demás tipos (Cuerda), pero algunos de los tipos se pueden convertir solo en cadena (MycustomType)

Para esto he agregado el método de miembro Convertir a hacia ResultType:

type MyTypes = 
   | Boolean       = 1
   | Integer       = 2
   | Decimal       = 3
   | Double        = 4
   | String        = 5
   | MyCustomType  = 6

type ResultType = 
   | Boolean of bool
   | Integer of int
   | Decimal of decimal
   | Double of double
   | String of string
   | MyCustomType of MyCustomType

   with 
     member this.ConvertTo(newType: MyTypes) = 
       match this with 
       | ResultType.Boolean(value) -> 
           match newType with 
           | MyTypes.Boolean -> 
              this
           | MyTypes.Integer -> 
              ResultType.Integer(if value then 1 else 0)
          ...
       | ResultType.MyCustomType(value) -> 
           match newType with 
           | MyTypes.MyCustomType -> 
              this
           | MyTypes.String -> 
              ResultType.String(value.ToString()) 
           | _ -> 
              failwithf "Conversion from MyCustomType to %s is not supported" (newType.ToString())

No me gusta tal construcción, porque si agrego más tipos, esto requiere que haga muchos cambios: Mytypes, Titype resultante y también en varios lugares del Convertir a función miembro.

¿Alguien puede sugerir una mejor solución para la conversión de tales tipos?

Gracias por adelantado

¿Fue útil?

Solución

Con un diseño ligeramente diferente, es posible explotar System.Convert.ChangeType y el hecho de que los constructores de sindicatos discriminados son en realidad funciones:

// statically typed wrapper for System.Convert.ChangeType
let conv a : 'T = System.Convert.ChangeType(a, typeof<'T>) :?> 'T

type MyCustomType() = class end

type ResultType = 
  | Boolean of bool
  | Integer of int
  | Decimal of decimal
  | Double of double
  | String of string
  | MyCustomType of MyCustomType
  with
    member this.ConvertTo (newType:'T->ResultType) =
      match this with
      | Boolean b -> newType( conv b )
      | Integer i -> newType( conv i )
      | Decimal d -> newType( conv d )
      | Double d -> newType( conv d )
      | String s -> newType( conv s )
      | MyCustomType m ->
         if typeof<'T> <> typeof<string> then
            raise (new System.InvalidCastException("MyCustomType can only be converted to String"))
         else
            String (m.ToString())

let i = Integer 42

let b = i.ConvertTo Boolean
printfn "%A" b

let d = i.ConvertTo Decimal
printfn "%A" d

let d2 = i.ConvertTo Double
printfn "%A" d2

let s = i.ConvertTo String
printfn "%A" s

//let mi = i.ConvertTo MyCustomType  // throws InvalidCastException

let m = MyCustomType (new MyCustomType())
let sm = m.ConvertTo String
printfn "%A" sm

//let im = m.ConvertTo Integer // throws InvalidCastException

Editar: una vez que agregue más tipos personalizados, esto no ayudará mucho.

Tal vez debería hacer que sus tipos personalizados sean implementados IConvertible. Luego puede eliminar el código de caso especial de ConvertTo y confiar completamente en System.Convert.ChangeType.

Aún tendrías que extender cada tipo personalizado ToObject Implementación siempre que agregue un nuevo tipo personalizado. Si eso es realmente mejor que un central ConvertTola función es discutible.

Otros consejos

¿Por qué quieres hacer una conversión de tipo para empezar? Los sindicatos discriminados son una buena forma de ocultar información de tipo hasta que la necesite y la complejidad abstracta. En general, tiene una declaración de coincidencia en una función que consume este tipo y luego solo emite si lo necesita.

Si está tratando de hacer algún tipo de analizador o motor de idioma, entonces no tiene más remedio que definir todo el elenco o al menos sus estados de error. Si no le importaría elaborar por qué / para qué usaría esto, tal vez podría sugerir otro enfoque.

Un aparte: F# y .NET en general no admiten la sobrecarga de los tipos de devolución.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top