F#中的NullReferenceException

时间:2011-03-28 16:57:03

标签: null f# nullreferenceexception

当我单步执行以下代码时,第二行的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 
        )

5 个答案:

答案 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)

由于taggedResearchReportoption类型,您希望在此处使用模式匹配:

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"

<强>更新

我在你添加的附加代码中有点迷失,但是你使用选项类型的方式肯定会有一些时髦的东西。使用模式匹配代替IsSomeOption.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似乎是一个错误的用例。