对于示例程序:
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
答案 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#的可能性 - 两者中最好的。