类型没有null作为适当的值

时间:2012-07-27 22:30:18

标签: f#

对于示例程序:

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< _>的值类型(struct)。需要。所以,请向我保证,我真的在处理一个引用类型,让我想知道为什么一个对象突然变成了这个工作。

更新:对于任何有兴趣的人,我将此作为公共服务定位器的一个解决方案,具有以下三个功能。请求的每个服务都必须支持 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

2 个答案:

答案 0 :(得分:42)

使用[<AllowNullLiteral>]属性:

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

默认情况下,F#类型不允许null(谢天谢地!)。此属性对于与其他.NET语言互操作很有用,并允许使用null进行赋值/比较。

答案 1 :(得分:17)

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#的可能性 - 两者中最好的。