类型没有null作为适当的值

Eri*_*son 24 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
Run Code Online (Sandbox Code Playgroud)

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

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

if instance :> obj = null then
Run Code Online (Sandbox Code Playgroud)

但是,我知道("感觉")这完全是"错误的".特别是当我考虑MyClass如何是一个不需要盒装的引用类型时(从C#背景说起).

我已经读过"F#Value Restriction"以及它如何影响类型推断,但我似乎无法理解它如何应用于这种情况.

问:还有其他办法吗?

除了#1:我发现了一种更简单的方法来获取错误......

type public MyClass(reasonForLiving:string) =
    member x.ReasonForLiving with get() = reasonForLiving
let nullMyClass : MyClass = null
Run Code Online (Sandbox Code Playgroud)

除了#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
Run Code Online (Sandbox Code Playgroud)

Dan*_*iel 45

使用[<AllowNullLiteral>]属性:

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

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

  • @Eric:可以将C#中的类,记录和DU设置为null,无论"AllowNullLiteral"的存在(或缺少) - 毕竟,它们都是引擎盖下的引用类型.该属性仅允许从F#_设置一些内容为null. (3认同)
  • 只是添加 - 如果不进行互操作,使用'Option't`更为惯用 (2认同)

ild*_*arn 17

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

假设这不适合您的用例,有一个简单的替代方案,具有不可观察的性能影响:

let inline isNull (x:^T when ^T : not struct) = obj.ReferenceEquals (x, null)
Run Code Online (Sandbox Code Playgroud)

然后,而不是做if instance = null then,if isNull instance then而是做.

这适用于任何引用类型(包括记录和DU),但不会引入将F#类型的对象设置为从F#中为空的可能性 - 两者中最好的.

  • 我曾经在一个大型项目上使用过这种方法,但最终后悔了.如果一个类型可以为null(在F#中或由于interop),那么它最好是显式的.一个例子是模式匹配.什么更好,定义一个空检查活动模式或`匹配值与null - > ...`?最后,这样的解决方法感到很骇人听闻.对于interop,只需在类型中添加`[<AllowNullLiteral>]`并包含null. (3认同)