Подтвердить что ты не робот

Тестирование нулевой ссылки в F #

Учитывая следующее:

[<DataContract>]
type TweetUser = {
    [<field:DataMember(Name="followers_count")>] Followers:int
    [<field:DataMember(Name="screen_name")>] Name:string
    [<field:DataMember(Name="id_str")>] Id:int
    [<field:DataMember(Name="location")>] Location:string}

[<DataContract>]
type Tweet = {
    [<field:DataMember(Name="id_str")>] Id:string
    [<field:DataMember(Name="text")>] Text:string
    [<field:DataMember(Name="retweeted")>] IsRetweeted:bool
    [<field:DataMember(Name="created_at")>] DateStr:string
    [<field:DataMember(Name="user", IsRequired=false)>] User:TweetUser
    [<field:DataMember(Name="sender", IsRequired=false)>] Sender:TweetUser
    [<field:DataMember(Name="source")>] Source:string}

Дезаминирование с помощью DataContractJsonSerializer(typeof<Tweet[]>) приведет к тому, что поле User или Sender будет пустым (по крайней мере, то, что говорит мне отладчик).

Если я попытаюсь написать следующее:

    let name = if tweet.User <> null 
                  then tweet.User.Name
                  else tweet.Sender.Name

компилятор испускает ошибку: "Тип" TweetUser "не имеет" null "в качестве правильного значения"

Как проверить нулевые значения в этом случае?

4b9b3361

Ответ 1

Чтобы циклически расширить ответ на @Tomas, -]

let name = if not <| obj.ReferenceEquals (tweet.User, null)
              then tweet.User.Name
              else tweet.Sender.Name

или

let inline isNull (x:^T when ^T : not struct) = obj.ReferenceEquals (x, null)

Unchecked.defaultof<_> делает правильную вещь и создает нули для ваших типов записей; проблема заключается в том, что оператор равенства по умолчанию использует общее структурное сравнение, которое ожидает, что вы всегда будете играть по правилам F # при использовании типов F #. В любом случае нулевая проверка действительно требует только сравнения ссылок.

Ответ 2

Чтобы добавить некоторые детали в комментарий от @ildjarn, вы получаете сообщение об ошибке, потому что F # не позволяет использовать null как значение типов, объявленных в F #. Мотивацией для этого является то, что F # пытается исключить значения nullNullReferenceException) из чистых программ F #.

Однако, если вы используете типы, которые не определены в F #, вам все же разрешено использовать null (например, при вызове функции, которая принимает System.Random в качестве аргумента, вы можете дать ей null), Это необходимо для взаимодействия, потому что вам может потребоваться передать null в библиотеку .NET или принять его в качестве результата.

В вашем примере TweetUser - это (тип записи), объявленный в F #, поэтому язык не позволяет обрабатывать null как значение типа TweetUser. Тем не менее, вы все равно можете получить значение null через ie Reflection или из кода С#, поэтому F # предоставляет "небезопасную" функцию, которая создает значение null любого типа, включая записи F #, которые обычно не имеют значения null, Это функция Unchecked.defaultOf<_>, и вы можете использовать ее для реализации вспомогательного помощника:

let inline isNull x = x = Unchecked.defaultof<_>

В качестве альтернативы, если вы помечаете тип с атрибутом AllowNullLiteral, то вы говорите компилятору F #, что он должен разрешать null как значение для этого конкретного типа, даже если это тип, объявленный в F # (и обычно это не разрешало бы null).

Ответ 3

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

isNull <| box obj

или

let isMyObjNull = isNull <| box obj

или

match box obj with
| isNull -> (* obj is null *)
| _ -> (* obj is not null *)