可区分联合中类型之间的转换

发布于 2024-10-22 04:46:59 字数 1414 浏览 2 评论 0原文

我有一个函数,它可以返回不同的类型,并且我使用可区分联合来实现此目的。我需要的是从可区分联合中的一种类型转换为另一种类型。 此外,某些类型可以转换为所有其他类型 (String),但某些类型只能转换为 String (MyCustomType)

为此,我添加了ConvertToResultType 的成员方法:

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())

我不喜欢这样的构造,因为如果我添加更多类型,这需要我做很多更改:MyTypes、ResultType 以及 ConvertTo 成员函数中的多个位置。

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

提前致谢

I have a function, which can returns different types, and I use discriminated union for this. What I need, is to have conversion from one type in discriminated union to another type.
Also some of the types can be convertable to all other types (String), but some of the types can be converted only to String (MyCustomType)

For this I've added member method ConvertTo to the 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())

I don't like such construction, because if I add more types, this requires me to do many changes: MyTypes, ResultType and also in several places in the ConvertTo member function.

Can anybody suggest better solution for such types conversion?

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

欢烬 2024-10-29 04:46:59

通过稍微不同的设计,可以利用 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 函数更好是有争议的。

With a slightly different design, it is possible to exploit System.Convert.ChangeType and the fact that the constructors of discriminated unions are actually functions:

// 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: Once you add more custom types, this will not help much.

Maybe you should make your custom types implement IConvertible. Then you can remove the special case code from ConvertTo and completely rely on System.Convert.ChangeType.

You would still have to extend every custom type's ToObject implementation whenever you add a new custom type. Whether that really is better than a central ConvertTofunction is debatable.

阳光的暖冬 2024-10-29 04:46:59

为什么要首先进行类型转换?可区分联合是隐藏类型信息直到您需要它并抽象复杂性的好方法。通常,您在使用此类型的函数中有一个匹配语句,然后仅在需要时才进行强制转换。

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

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

Why are you wanting to do type conversion to begin with? Discriminated Unions are a good way of hiding type information until you need it and abstract complexity away. Generally you have a match statement in a function that consumes this type and then you only cast if you need to.

If you're trying to make some type of parser or language engine then you have no choice but to define all the cast or at least their error states. If you wouldn't mind elaborating on why / what you would use this for, maybe I could suggest another approach.

An aside: F# and .NET in general doesn't support overloading of return types.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文