Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NullReferenceException in F#

When I step through the following code, report on the second line is null. However, the third line generates a NullReferenceException.

member this.setTaggedResearchReportList (index : int) (taggedResearchReport : TaggedResearchReportUIVO option) =
    let report = Option.get(taggedResearchReport)
    if not(report.Equals(null)) then
        // do some stuff here

Why is that, and what can I do to avoid it? Thanks!

Added later:

Here's the line that calls this.setTaggedResearchReportList:

getMostRecentTaggedResearchReportForSecurityId (item.id) (new Action<_>(this.setTaggedResearchReportList 0))

Here's the getMostRecentTaggedResearchReportForSecurityId method:

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 
        )
like image 245
Mike Cialowicz Avatar asked Aug 25 '26 20:08

Mike Cialowicz


2 Answers

This isn't an answer per se, but to add to the discussion of null handling, particularly when interop-ing with C# code: I like to avoid using the [<AllowNullLiteral>] attribute and define a module such as the following to isolate the use of null in F# code.

[<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

In general, keep these checks as close to the interface of your API as possible and you can typically forget about null within F#, due to preserving the compiler's null checks.

like image 196
Daniel Avatar answered Aug 27 '26 15:08

Daniel


Your TaggedResearchReportUIVO type is evidently defined in F#, and doesn't allow null as a proper value. Therefore, the compiler will prevent you from using the literal null as a value of that type; however, some other code is going behind the compiler's back and sticking a null value in there. To work around the immediate issue, you can try comparing against Unchecked.defaultof<TaggedResearchReportUIVO> instead of null.

However, it's probably worth assessing whether you should be making some more significant changes to avoid this type of issue in the first place. For instance, if it makes sense to have null as a proper value of type TaggedResearchReportUIVO, then you could add the [<AllowNullLiteral>] attribute to the definition of that type. Alternatively, if it really doesn't make sense to use null as a proper value, then you need to investigate the code which is generating the problematic value.

As an aside, there are other parts of your code that can be cleaned up considerably. For example, consider changing

fun (x : option<JSONSingleResult<TaggedResearchReportUIVO>>) -> 
    match x.IsSome with
    | true -> Some(Option.get(x).result)
    | false -> None

to

Option.map (fun (jsr : JSONSingleResult<_>) -> jsr.result)
like image 44
kvb Avatar answered Aug 27 '26 15:08

kvb



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!