4

我有一个函数,它可以返回不同的类型,为此我使用了有区别的联合。我需要的是从有区别的联合中的一种类型转换为另一种类型。还有一些类型可以转换为所有其他类型(String),但一些类型只能转换为 String (MyCustomType

为此,我将成员方法ConvertTo添加到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())

我不喜欢这样的构造,因为如果我添加更多类型,这需要我做很多更改:MyTypesResultType以及ConvertTo成员函数的几个地方。

任何人都可以为这种类型转换提出更好的解决方案吗?

提前致谢

4

2 回答 2

8

通过稍微不同的设计,可以利用System.Convert.ChangeType可区分联合的构造函数实际上是函数的事实:

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

编辑:添加更多自定义类型后,这将无济于事。

也许你应该让你的自定义类型实现IConvertible。然后,您可以从中删除特殊情况代码ConvertTo并完全依赖System.Convert.ChangeType.

ToObject每当您添加新的自定义类型时,您仍然必须扩展每个自定义类型的实现。这是否真的比中心ConvertTo功能更好是值得商榷的。

于 2011-03-15T18:57:02.693 回答
2

为什么要开始进行类型转换?有区别的联合是隐藏类型信息的好方法,直到您需要它并抽象出复杂性。通常,您在使用此类型的函数中有一个 match 语句,然后仅在需要时进行转换。

如果您正在尝试制作某种类型的解析器或语言引擎,那么您别无选择,只能定义所有强制转换或至少定义它们的错误状态。如果您不介意详细说明为什么/为什么要使用它,也许我可以建议另一种方法。

顺便说一句:F# 和 .NET 通常不支持返回类型的重载。

于 2011-03-15T15:11:38.523 回答