Question

J'ai une fonction, qui peut les rendements différents types, et je l'utilise union discriminée pour cela. Ce que je dois, est d'avoir une conversion d'un type en union discriminée à un autre type. En outre, certains des types peuvent être à tous les autres convertable types ( Chaîne ), mais certains types peuvent être convertis uniquement à la chaîne ( MyCustomType )

Pour cela, j'ai ajouté méthode membre ConvertTo à la 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())

Je ne aime pas cette construction, parce que si j'ajouter plusieurs types, cela me demande de faire beaucoup de changements: MyTypes , ResultType et aussi à plusieurs endroits dans la ConvertTo fonction membre.

Quelqu'un peut-il suggérer une meilleure solution pour la conversion de ces types?

Merci à l'avance

Était-ce utile?

La solution

Avec un design légèrement différent, il est possible d'exploiter System.Convert.ChangeType et le fait que les constructeurs des syndicats discriminés sont en fait des fonctions:

// 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

EDIT:. Une fois que vous ajoutez des types personnalisés, ce ne sera pas un grand secours

Peut-être que vous devriez faire vos types personnalisés mettre en œuvre IConvertible. Ensuite, vous pouvez supprimer le code de cas particulier de ConvertTo et compter entièrement sur System.Convert.ChangeType.

Vous auriez encore à étendre chaque mise en œuvre de ToObject de type personnalisé à chaque fois que vous ajoutez un nouveau type personnalisé. Que ce soit vraiment mieux qu'un ConvertTofunction central est discutable.

Autres conseils

Pourquoi êtes-vous envie de faire une conversion de type pour commencer? Les syndicats discriminées sont une bonne façon de cacher des informations de type jusqu'à ce que vous en avez besoin et de la complexité abstraire. En général, vous avez une déclaration de match dans une fonction qui consomme ce type et vous ne lancez si vous avez besoin.

Si vous essayez de faire un certain type de moteur d'analyse syntaxique ou de la langue, alors vous avez pas d'autre choix que de définir tous les acteurs ou tout au moins leurs états d'erreur. Si vous ne me dérangerait pas d'élaborer pourquoi / ce que vous utiliser pour, je pourrais peut-être proposer une autre approche.

Un côté:. F # et .NET en général ne prend pas en charge la surcharge des types de retour

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top