NullReferenceException в F # - PullRequest
       36

NullReferenceException в F #

1 голос
/ 28 марта 2011

Когда я выполняю следующий код, 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 ]

7 голосов
/ 28 марта 2011

Это не сам по себе ответ, а добавление к обсуждению нулевой обработки, особенно при взаимодействии с кодом C #: я бы хотел избежать использования атрибута [<AllowNullLiteral>] и определить такой модуль, как следующееизолируйте использование null в коде F #.

[<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, и вы обычно можете забыть о null в F # из-засохранение нулевых проверок компилятора.

4 голосов
/ 28 марта 2011

Поскольку 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.например, в вашем лямбда-выражении должно выглядеть больше так:

(fun (x : option<JSONSingleResult<TaggedResearchReportUIVO>>) ->
            match x with
                | Some(value) -> Some(value.result)
                | None -> None 
        )

Я не уверен, что это решит ваши проблемы, но это начало.

4 голосов
/ 28 марта 2011

Ваш тип 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)
2 голосов
/ 28 марта 2011

Как отмечали другие, проблема в том, что десериализатор может возвращать null, но, поскольку вы работаете с типами F #, вы не можете напрямую проверить, является ли значение null!

Я думаю, что лучший способ - как можно раньше избавиться от значения null, поступающего из некоторых библиотек .NET, чтобы вы могли сохранить оставшуюся часть кода в чистоте.Вот как я думаю, это можно исправить:

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).

1 голос
/ 28 марта 2011

Если report равно нулю, вы не можете вызвать report.Equals, поэтому код бесполезен.Измените его на report = null.Кроме того, определение taggedResearchReport как option и проверка его на null представляется неправильным вариантом использования.

...