当我单步执行以下代码时,第二行的report
为空。
但是,第三行生成NullReferenceException。
member this.setTaggedResearchReportList (index : int) (taggedResearchReport : TaggedResearchReportUIVO option) =
let report = Option.get(taggedResearchReport)
if not(report.Equals(null)) then
// do some stuff here
为什么会这样,我该怎么办才能避免呢?谢谢!
以下是调用this.setTaggedResearchReportList
的行:
getMostRecentTaggedResearchReportForSecurityId (item.id) (new Action<_>(this.setTaggedResearchReportList 0))
以下是getMostRecentTaggedResearchReportForSecurityId
方法:
let getMostRecentTaggedResearchReportForSecurityId (securityId : int) (callbackUI : Action<_>) =
getSingleRPCResult<JSONSingleResult<TaggedResearchReportUIVO>, TaggedResearchReportUIVO>
"TaggedResearchReportRPC"
"getMostRecentResearchReportForSecurityId"
(sprintf "%i" securityId)
callbackUI
(fun (x : option<JSONSingleResult<TaggedResearchReportUIVO>>) ->
match x.IsSome with
| true -> Some(Option.get(x).result)
| false -> None
)
答案 0 :(得分:7)
这本身不是答案,而是添加到空处理的讨论中,特别是在与C#代码交互时:我喜欢避免使用[<AllowNullLiteral>]
属性并定义一个模块,例如以下是在F#代码中隔离使用null
。
[<AutoOpen>]
module Interop =
let inline isNull value = System.Object.ReferenceEquals(value, null)
let inline nil<'T> = Unchecked.defaultof<'T>
let inline safeUnbox value = if isNull value then nil else unbox value
let (|Null|_|) value = if isNull value then Some() else None
type Foo() = class end
type Test() =
member this.AcceptFoo(foo:Foo) = //passed from C#
if isNull foo then nullArg "foo"
else ...
member this.AcceptFoo2(foo:Foo) = //passed from C#
match foo with
| Null -> nullArg "foo"
| _ -> ...
member this.AcceptBoxedFoo(boxedFoo:obj) =
let foo : Foo = safeUnbox boxedFoo
...
member this.ReturnFoo() : Foo = //returning to C#
if (test) then new Foo()
else nil
通常,请将这些检查尽可能靠近API的接口,并且由于保留了编译器的空检查,您通常可以忘记F#中的null
。
答案 1 :(得分:4)
由于taggedResearchReport
是option
类型,您希望在此处使用模式匹配:
member this.setTaggedResearchReportList (index : int) (taggedResearchReport : TaggedResearchReportUIVO option) =
match taggedResearchReport with
| Some(report) -> //do some stuff when we have "something"
| None -> //do some different stuff when we have "nothing"
<强>更新强>
我在你添加的附加代码中有点迷失,但是你使用选项类型的方式肯定会有一些时髦的东西。使用模式匹配代替IsSome
和Option.get
。例如在你的lambda表达式中看起来应该更像这样:
(fun (x : option<JSONSingleResult<TaggedResearchReportUIVO>>) ->
match x with
| Some(value) -> Some(value.result)
| None -> None
)
我不确定这是否会解决您的问题,但这是一个开始。
答案 2 :(得分:4)
您的TaggedResearchReportUIVO
类型显然是在F#中定义的,并且不允许null
作为正确的值。因此,编译器将阻止您使用文字null
作为该类型的值;但是,其他一些代码正在编译器的后面,并在那里粘贴一个空值。要解决这个问题,您可以尝试与Unchecked.defaultof<TaggedResearchReportUIVO>
进行比较,而不是null
。
然而,可能值得评估您是否应首先进行一些更重要的更改以避免此类问题。例如,如果将null
作为TaggedResearchReportUIVO
类型的正确值是有意义的,那么您可以将[<AllowNullLiteral>]
属性添加到该类型的定义中。或者,如果将null
用作正确的值确实没有意义,那么您需要调查生成有问题值的代码。
顺便说一下,代码的其他部分可以大大清理。例如,考虑更改
fun (x : option<JSONSingleResult<TaggedResearchReportUIVO>>) ->
match x.IsSome with
| true -> Some(Option.get(x).result)
| false -> None
到
Option.map (fun (jsr : JSONSingleResult<_>) -> jsr.result)
答案 3 :(得分:2)
正如其他人所指出的,问题是反序列化器可能会返回null
,但由于您使用的是F#类型,因此无法直接检查该值是否为null
!
我认为最好的方法是尽早摆脱某些.NET库的null
值,以便您可以保持其余的代码清洁。以下是我认为可以解决的问题:
let getMostRecentTaggedResearchReportForSecurityId
(securityId : int) (callbackUI : Action<_>) =
getSingleRPCResult< JSONSingleResult<TaggedResearchReportUIVO>,
TaggedResearchReportUIVO >
"TaggedResearchReportRPC"
"getMostRecentResearchReportForSecurityId"
(sprintf "%i" securityId)
callbackUI
(fun (x : option<JSONSingleResult<TaggedResearchReportUIVO>>) ->
// Note: Return 'Some' only when the result is 'Some' and the 'result'
// value carried inside the discriminated union is not 'null'
match x with
| Some(res) when
// Make sure that there are no dangerous values (I suppose
// JSONSingleResult is imported from .NET and
// TaggedResearchReportUIVO is your F# type.
res <> null && res.result <> Unchecked.defaultOf<_> ->
Some(res.result)
| _ -> None )
然后您可以安全地使用模式匹配来处理该值,因为它永远不会是null
:
member this.setTaggedResearchReportList
(index : int) (taggedResearchReport : TaggedResearchReportUIVO option) =
match taggedResearchReport with
| Some(report) ->
// do some stuff here
| _ -> () // do nothing here
我不太可能使用AllowNullLiteral
,因为唯一可以解决此问题的地方是从JSON库获取结果。一旦解决了问题,就可以安全地在F#代码中的其他地方使用该值(无需检查null
)。
答案 4 :(得分:1)
如果report
为空,则无法调用report.Equals
,因此代码无用。将其更改为report = null
。此外,将taggedResearchReport
定义为option
并将其检查为null
似乎是一个错误的用例。