可区分联合中类型之间的转换
-
24-10-2019 - |
题
我有一个函数,它可以返回不同的类型,我使用 受歧视联盟 为了这。我需要的是从可区分联合中的一种类型转换为另一种类型。此外,某些类型可以转换为所有其他类型(细绳),但有些类型只能转换为 String (我的自定义类型)
为此我添加了成员方法 转换成 到 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())
我不喜欢这样的构造,因为如果我添加更多类型,这需要我做很多更改: 我的类型, 结果类型 并且在国内的一些地方也 转换成 成员函数。
有人能为这种类型转换提出更好的解决方案吗?
提前致谢
解决方案
通过稍微不同的设计,可以利用 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
功能有争议。
其他提示
为什么要首先进行类型转换?可区分联合是隐藏类型信息直到您需要它并抽象复杂性的好方法。通常,您在使用此类型的函数中有一个匹配语句,然后仅在需要时才进行强制转换。
如果您尝试制作某种类型的解析器或语言引擎,那么您别无选择,只能定义所有转换或至少定义它们的错误状态。如果您不介意详细说明为什么/要使用它做什么,也许我可以建议另一种方法。
旁白:F# 和 .NET 通常不支持返回类型的重载。
不隶属于 StackOverflow