如何使用Option.map进行空条件?F# 如何获取空对象的属性?错误?



以下代码返回Some "Test"而不是None。基本上,我正在尝试实现cObj?.B.A.P的 C# 代码。

// Setup
[<AllowNullLiteral>]
type A() =
member x.P = "Test"
[<AllowNullLiteral>]
type B(a:A) =
member x.A = a
[<AllowNullLiteral>]
type C(b:B) =
member x.B = b
// Test
let aObj: A = null
let cObj = new C(new B(aObj))
let r = 
cObj |> Option.ofObj
|> Option.map(fun c -> c.B)
|> Option.map(fun b -> b.A) 
|> Option.map(fun a -> a.P) // Expect return None since a is null
// printfn "%A" a; will print <null>.
// How can F# got property of null object?
r

似乎 F# 不会将null视为 Option.map 中的None。有没有一个简单的修复程序可以让它在找到null后立即返回 None?

与 C# 不同,F# 试图在任何地方都显式。从长远来看,这会导致更易于维护和正确的程序。

特别是,nullOption完全无关.nullNone不同。None是类型Option的值,而null是一个非常模糊的概念 - 可以是任何类型的值。

如果您想在参数null时返回None,否则Some,您需要的是Option.bind,而不是Option.mapOption.bind接受一个函数,该函数获取一个值(从前一个Option中提取(并返回另一个Option。像这样:

let maybeC = Option.ofObj cObj
let maybeB = maybeC |> Option.bind (c -> Option.ofObj c.B)
let maybeA = maybeB |> Option.bind (b -> Option.ofObj b.A)
let maybeP = maybeA |> Option.bind (a -> Option.ofObj a.P)

或一气呵成:

let maybeP = 
Option.ofObj cObj
|> Option.bind (c -> Option.ofObj c.B)
|> Option.bind (b -> Option.ofObj b.A)
|> Option.bind (a -> Option.ofObj a.P)

如果您经常执行此类操作,则可以将Option.bindOption.ofObj调用组合在一起,并将其编码为单独的函数:

let maybeNull f = Option.bind (x -> Option.ofObj (f x))
let maybeP =
Option.ofObj cObj
|> maybeNull (c -> c.B)
|> maybeNull (b -> b.A)
|> maybeNull (a -> a.P)

但是,如果你发现自己被埋没在这样的null中,我建议也许你的领域设计没有经过深思熟虑。空值不是一个好的建模工具,应尽可能避免它们。我鼓励你重新考虑你的设计。

Option.map 的第一个参数是一个函数'T -> 'U。 它的参数类型是'T,而不是'T option。 因此,在上一个 lambdafun a -> a.P中,空参数表示类型A的空,而不是类型A option的空。

由于类型A的成员P仅返回字符串"Test",因此即使接收方为 null,调用也会成功并返回。 如果您尝试在P的正文中使用 self 标识符,您将获得空引用异常。

相关内容

最新更新