问题摘要
在使用f#时,我必须明确地将值强制转换为其类型的父类型,以便使模式匹配表达式正确地进行类型检查.理想情况下,我会喜欢一种更简洁的方式.
例
假设我有一些类层次结构:
type Foo () =
abstract member Value : unit -> string
type A (i:int) =
inherit Foo ()
override this.Value () = i.ToString()
type B (s:string) =
inherit Foo ()
override this.Value () = s
Run Code Online (Sandbox Code Playgroud)
理想情况下,在一般的编程语言中,我会编写以下内容:
let bar (i:int) : Foo =
match i with
| 1 -> B "one"
| _ -> A i
Run Code Online (Sandbox Code Playgroud)
然而,这无法正确键入检查,给我错误,"这个表达式应该有类型Foo但这里有类型B".我不明白为什么编译器没有足够的信息来推断匹配表达式的公共超类型,然后检查公共超类型是否为'Foo'.
目前,我被迫为模式匹配中的每个案例提供明确的强制:
let bar2 (i:int) : Foo =
match i with
| 1 -> (B "one") :> Foo
| _ -> (A i) :> Foo
Run Code Online (Sandbox Code Playgroud)
我想避免这个.
进一步说明
感谢您对此事的任何帮助.
我会用upcast
,一个
[<AbstractClass>]
type Foo () =
abstract member Value : unit -> string
type A (i:int) =
inherit Foo ()
override this.Value () = i.ToString()
type B (s) =
inherit Foo ()
override this.Value () = s
let bar2 i : Foo =
match i with
| 1 -> upcast B "one"
| _ -> upcast A i
Run Code Online (Sandbox Code Playgroud)
你仍然需要将它添加到每个分支,但这通常比转换为类型更好,因为typename通常是20或30个字符long(MyNamespace.ThisThingy
),而upcast
只是6个字符.
但是,简而言之,语言规则不允许任何其他内容,所有分支的类型必须相同.
我之前已经看过几次这个问题了,但我刚才意识到有一种有趣的方法来解决这个问题(没有任何重大的负面影响,比如大的运行时间开销).
您可以使用只有成员的非常简单的计算表达式Return
.构建器将具有类型参数,并且Return
将期望此类型的值.诀窍是,F#在调用成员时会插入自动向上转换.这是宣言:
type ExprBuilder<'T>() =
member x.Return(v:'T) = v
let expr<'T> = ExprBuilder<'T>()
Run Code Online (Sandbox Code Playgroud)
要编写一个返回任何内容的简单模式匹配obj
,您现在可以编写:
let foo a = expr<obj> {
match a with
| 0 -> return System.Random()
| _ -> return "Hello" }
Run Code Online (Sandbox Code Playgroud)
您仍然必须明确返回类型(在创建计算表达式时),但我发现语法非常整洁(但它绝对是一个棘手的用法,可能会让那些第一次看到它的人感到困惑).