29

对于示例程序:

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's why OO languages die from bugs"))
    instance

当我将此类用作隐式类型函数的返回值并将其与null进行比较时,出现错误“'MyClass' 类型没有 null 作为正确的值” (b/c 与 C# 依赖注入的兼容性要求我不能依赖 F# 选项类型)。

我可以通过将空检查更改为:

if instance :> obj = null then

但是,我知道(“感觉”)这完全是“错误的”。特别是当我考虑 MyClass 是一个不需要装箱的引用类型时(从 C# 背景说)。

我已经阅读了“F# 值限制”以及它如何影响类型推断,但我似乎无法看到它如何应用于这种场景。

问:还有其他方法可以做到这一点吗?

除了#1:我找到了一个更简单的方法来获取错误......

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

除了#2:我确实尝试了 System.Nullable 没有考虑...... MyClass 是一个引用类型,而不是 Nullable<_> 需要的值类型(结构)。所以,只是让我放心,我真的在处理引用类型,让我想知道为什么对象强制转换突然使这项工作有效。

更新:对于任何感兴趣的人,我将其用作具有以下三个功能的 Common Service Locator 的一种解决方案。每个请求的服务都必须支持null,因此如果服务类是在 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
4

2 回答 2

49

使用[<AllowNullLiteral>]属性:

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

默认情况下,F# 类型不允许 null(感谢上帝!)。此属性对于与其他 .NET 语言的互操作很有用,并允许与 null 进行分配/比较。

于 2012-07-27T22:32:07.830 回答
21

AllowNullLiteral属性的问题在于,除了允许您将对象与 null 进行比较之外,它还可以您的对象设置null。

假设这对于您的用例来说是不可取的,有一个简单的替代方案,具有不可观察的性能影响:

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

然后与其做if instance = null then,不如做if isNull instance then

这适用于任何引用类型(包括记录和 DU),但不会引入将 F# 类型的对象从 F# 设置为 null 的可能性——两全其美。

于 2012-07-27T23:39:58.083 回答