以下代码返回conformed_name = "MISCELLANEOUS SUBJECT CORP"
CIK = "000000000"
IRS_number = "123456789"
fiscal_year_end = "1231"
而不是Some "Test"
。基本上,我正在尝试实现None
的C#代码。
cObj?.B.A.P
在Option.map中,似乎F#不会将// 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
视为null
。是否有一个简单的解决方法,使它在找到None
后立即返回None?
答案 0 :(得分:5)
与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)
或一口气:
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.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)
但是,如果您发现自己陷入null
之类的情况,我建议也许您的域设计没有被很好地考虑。空值不是一个好的建模工具,应尽可能避免使用它们。我鼓励您重新考虑您的设计。
答案 1 :(得分:0)
Option.map的第一个参数是函数'T -> 'U
。其参数的类型为'T
,而不是'T option
。因此,在您的最后一个lambda fun a -> a.P
中,null参数表示类型为A
的null,而不是类型为A option
的null。
因为类型P
的成员A
仅返回字符串“ Test”,所以即使接收者为null,调用也会成功并返回。如果尝试在P
的正文中使用self标识符,则将获得null引用异常。