Exception handling 如何在F中实现空安全运算符#

Exception handling 如何在F中实现空安全运算符#,exception-handling,f#,operator-overloading,nullreferenceexception,quotations,Exception Handling,F#,Operator Overloading,Nullreferenceexception,Quotations,我想知道您是否可以在F#中添加类似于“null-safe”运算符的内容。我知道,在下一个更大的版本中,可能有计划在C#中设置这样一个运营商 如果没有办法实现这种行为,那么有没有办法将可能抛出NullReferenceException的语句包装到捕获异常并仅返回null的块中 我想到了这样的事情: 但这并不是我想要的(实际上它甚至没有编译:) 我通读了: 还有更多的msdn文档 有人知道如何为链式方法优雅地创建这样一行try-catch吗?我不认为您可以实现类似于为C提出的?操作符的东

我想知道您是否可以在F#中添加类似于“null-safe”运算符的内容。我知道,在下一个更大的版本中,可能有计划在C#中设置这样一个运营商

如果没有办法实现这种行为,那么有没有办法将可能抛出
NullReferenceException
的语句包装到捕获异常并仅返回
null
的块中

我想到了这样的事情:

但这并不是我想要的(实际上它甚至没有编译:)

我通读了:

  • 还有更多的msdn文档

有人知道如何为链式方法优雅地创建这样一行try-catch吗?

我不认为您可以实现类似于为C提出的
操作符的东西,以一种实用且语法方便的方式将F作为库特性。尽管如此,我认为它将是一个非常有用的语言扩展,所以我将把它提交给

异常处理。您的代码示例与C#做得不太一样,因为您只是在处理任意异常。C#特性的要点是,它在每次
检查后插入
null
检查-您可以通过转换引号然后编译它来实现这一点,但编译速度会很慢。您的功能实际上可以是:

let orNull f = try f () with :? NullReferenceException -> null
然后你就可以写了

orNull <| fun () -> x.Foo().Bar()
这可以用与C#
操作符相同的方式执行空检查,但是您需要为方法调用链的每个部分单独绑定,因此需要更多的类型。计算生成器在
Bind
成员中插入一个隐藏的空检查,如下所示:

safe { let! a = x.Foo()
       let! b = a.Bar()
       return b }
type NullBuilder() = 
  member x.Return(v) = v
  member x.Bind(v, f) = if (box v) = null then null else f v 

let safe = NullBuilder()
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))
也许可以使用F#worflows,扩展May monad的概念,并能够写出以下内容:

let sth = maybe { someobject.method().another().another() }

这是一个有趣的问题。幸运的是,仅存在于F#world中的代码(即不使用以其他语言编写的.NET库)并不存在
想要解决的普遍问题,因为空性必须是显式的(使用
[]
)。但是,这个概念有一个功能性的对应关系:<代码>选项>代码>类型和<代码> BION/COD>函数(<代码> BIN < /代码>是工作流的基础,如托马斯所示)。p> 你可以这样做:

safe { let! a = x.Foo()
       let! b = a.Bar()
       return b }
type NullBuilder() = 
  member x.Return(v) = v
  member x.Bind(v, f) = if (box v) = null then null else f v 

let safe = NullBuilder()
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
  • 这在函数式语言中通常是如何处理的

我不得不说,虽然这看起来像是一个有用的操作符,但我发现实际上它不是(Groovy有这样一个操作符,我经常接触它)。对于初学者来说,滥用它的时机已经成熟:许多程序员将盲目地到处使用操作符,而不是仔细考虑方法的契约。这会导致传播空值,而实际上空指针异常是合适的。然后很难调试违反空输入的错误契约的确切来源。非常好的一点!劳伦特·勒布伦(Laurent Le Brun)也写了一篇关于这一点的文章:谢谢你的回答。我对选项和绑定的理解更为清晰:但正如您所提到的,在处理我的问题时,我看不到使用这种结构有什么好处。我使用F#的原因是我想编写更明确的代码,而不是复杂的代码。首先,并非所有读者都清楚使用Option+Bind,因此我可能会继续使用simple
try。。抓住。。使用