下面的代码返回Some "Test"代替None.基本上,我正在尝试实现C#代码cObj?.B.A.P.
// 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
Run Code Online (Sandbox Code Playgroud)
这似乎F#不把null作为None在Option.map.是否有一个简单的修复方法,一旦null找到它就会返回None ?
与C#不同,F#试图在任何地方都是明确的.从长远来看,这会带来更多可维护和正确的程序.
特别是,null绝对没有任何关系Option.null是不一样的None.None是一个类型的值Option,而null这是一个非常模糊的概念 - 一个可以是任何类型的值.
如果您想返回None时的说法是null和Some否则,你需要的是Option.bind,没有Option.map.Option.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)
Run Code Online (Sandbox Code Playgroud)
或者一气呵成:
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)
Run Code Online (Sandbox Code Playgroud)
如果你经常这样做,你可以组合Option.bind,Option.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)
Run Code Online (Sandbox Code Playgroud)
但是,如果你发现自己被埋没了null,我会建议你的域名设计可能没有经过深思熟虑.Nulls不是一个好的建模工具,应该尽可能避免它们.我鼓励你重新考虑你的设计.