我想知道你是否可以在F#中添加类似" null-safe" -operator的内容。我知道可能有计划在C#中使用这样一个运算符来进行下一个更大的发布。
如果没有办法实现这样的行为,是否有办法将一个可能抛出NullReferenceException
的语句包装成一个捕获异常并只返回null
的块。
我想到了类似的东西:
let sth = %% <@ someobject.method().another().andAnother() @>
其中%%
是执行表达式并检查异常的自定义运算符。
我做的一个尝试是:
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.QuotationEvaluation
let (~%%) (right:Expr) =
try
right.CompileUntyped()
right()
with
| ex -> null
但这不符合我的要求(实际上它甚至没有编译:)
我通读了:
有没有人有想法如何优雅地为链式方法创建这样的单行try-catch?
答案 0 :(得分:5)
我认为你不能为F#提供类似.?
运算符的东西,这在实际上非常有用且语法上方便F#作为库特性。虽然,我认为这将是一个非常有用的语言扩展,所以我将其提交给F# user voice,也许submit a pull request: - )。
异常处理。您的代码示例与C#完全不同,因为您只是在处理任意异常。 C#功能的关键是它在每个null
之后插入 .
检查 - 您可以通过转换引号然后编译它来做到这一点,但编译将会慢一点你的功能可能只是:
let orNull f = try f () with :? NullReferenceException -> null
然后你可以写
orNull <| fun () -> x.Foo().Bar()
..但如前所述,这只是将代码包装在标准try
块中并处理异常,因此它不会像C#代码那样做。
计算构建器。如果你想要一个更高级的F#解决方案,你可以想出一个计算表达式构建器,它可以让你写这样的东西:
safe { let! a = x.Foo()
let! b = a.Bar()
return b }
这可以像C#.?
运算符一样进行空值检查,但是对于方法调用链的每个部分都需要单独的绑定,因此需要更多的输入。计算构建器在Bind
成员中插入隐藏的空检查,如下所示:
type NullBuilder() =
member x.Return(v) = v
member x.Bind(v, f) = if (box v) = null then null else f v
let safe = NullBuilder()
答案 1 :(得分:3)
这是一个有趣的问题。幸运的是,仅存在于F#世界中的代码(即不使用其他语言编写的.NET库)并没有.?
旨在解决的普遍问题,因为可空性必须是明确的(使用[<AllowNullLiteral>]
)。但是,这个概念还有一个功能:option
类型和bind
函数(bind
是工作流的基础,如Tomas所示)。
你可以采取这样的方式:
type T() =
member this.M(b) = if b then Some this else None
T().M(true)
|> Option.bind (fun t -> t.M(true))
|> Option.bind (fun t -> t.M(false))
|> Option.bind (fun t -> t.M(true))
并定义bind
运算符
let (?>) o f = Option.bind f o
并将其缩短为:
T().M(true)
?> fun t -> t.M(true)
?> fun t -> t.M(false)
?> fun t -> t.M(true)
但是,这样做的价值非常小。它既不简洁也不清晰。但是,我认为它证明了:
null
答案 2 :(得分:1)
也许使用F#worflows,扩展了Maybe monad的想法,并且能够写下这个:
let sth = maybe { someobject.method().another().another() }