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

Тип не имеет нулевого значения в качестве правильного значения

Для примера программы:

type public MyClass(reasonForLiving:string) =
    member x.ReasonForLiving with get() = reasonForLiving

let classFactory () = MyClass("up to you")
let live () =
    let instance = classFactory()
    if instance = null then raise(System.Exception("null is not living... that why OO languages die from bugs"))
    instance

Я получаю сообщение об ошибке "Тип MyClass не имеет нулевого значения в качестве правильного значения", когда я использую этот класс как возвращаемое значение неявно типизированных функций и сравниваю его с нулевым (b/c требований совместимости с С# зависимая инъекция Я не могу полагаться на типы опций F #).

Я легко могу исправить это, изменив нулевую проверку на:

if instance :> obj = null then

Однако, я знаю ( "чувствую" ), это полностью "неправильно". Особенно, когда я рассматриваю, как MyClass является ссылочным типом, который не нужно вставлять в бокс (говоря с фона С#).

Я читал о "Ограничении значения F #" и о том, как он влияет на вывод типа, но я не могу понять, как он относится к этому сценарию.

Q: Есть ли другой способ сделать это?

Помимо # 1: я нашел более простой способ получения ошибки...

type public MyClass(reasonForLiving:string) =
    member x.ReasonForLiving with get() = reasonForLiving
let nullMyClass : MyClass = null

Помимо # 2: я попробовал System.Nullable, не задумываясь... MyClass - это ссылочный тип, а не тип значения (struct), который требуется Nullable < _ > . Итак, просто успокаивает меня, что я ДЕЙСТВИТЕЛЬНО имею дело с ссылочным типом, и оставляет меня в недоумении, почему внезапно этот объект делает эту работу.

Обновление:. Для всех, кого это интересует, я использовал это как одно решение для Common Service Locator с тремя функциями ниже. Каждая запрошенная служба должна поддерживать нуль, поэтому, если класс службы определен в F #, вам нужно добавить [<AllowNullLiteral>]:

let private getServiceLocator () =
    try Some(Microsoft.Practices.ServiceLocation.ServiceLocator.Current)
    with | _ -> None

let private getService serviceFactory =
    let serviceLocator = getServiceLocator()
    let service = match serviceLocator with 
                  | None -> serviceFactory()
                  | _ -> 
                    match serviceLocator.Value.GetInstance<'a>() with
                    | null -> serviceFactory()
                    | svc -> svc
    match service with
    | null -> None
    | _ -> Some(service)

let private getRequiredService serviceFactory =
    let service = getService serviceFactory
    match service with
    | None -> raise(MissingServiceException(""))
    | _ -> service.Value
4b9b3361

Ответ 1

Используйте атрибут [<AllowNullLiteral>]:

[<AllowNullLiteral>]
type public MyClass(reasonForLiving:string) =
    member x.ReasonForLiving with get() = reasonForLiving

По умолчанию типы F # не допускают null (слава богу!). Этот атрибут полезен для взаимодействия с другими языками .NET и позволяет присваивать/сравнивать с нулевым.

Ответ 2

Проблема с атрибутом AllowNullLiteral заключается в том, что в дополнение к тому, чтобы вы могли сравнивать свои объекты с нулевым значением, это также позволяет установить ваши объекты в null.

Предполагая, что это нежелательно для вашего прецедента, есть простая альтернатива с ненаблюдаемым воздействием на производительность:

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

Вместо этого вместо if instance = null then сделайте if isNull instance then.

Это будет работать для любого ссылочного типа (включая записи и DU), но не предусматривает возможность установки объектов ваших типов F # на null из F # - лучшего из обоих миров.