2012-07-27 32 views
21

對於樣本程序:類型沒有空作爲一個適當的值

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 

我得到錯誤「的類型‘MyClass的’沒有空作爲一個適當的值」,當我去使用這個類作爲隱式類型函數的返回值,並將其與null(與C#依賴注入的兼容性需求的b/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的是引用類型,而不是可空值< _>所需的值類型(結構)。所以,只是讓我確信我真的在處理一個引用類型,並讓我想知道爲什麼一個對象會突然產生這個工作。

更新:對於任何有興趣的人,我將它作爲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 

回答

39

使用[<AllowNullLiteral>]屬性:

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

默認情況下,F#類型不要讓null(謝天謝地!)。此屬性對於與其他.NET語言的互操作非常有用,並允許使用null進行賦值/比較。

+0

Doh!看到這個之後,我從MSDN的第三句話「空值(F#)」中識別出它:http://msdn.microsoft.com/en-us/library/dd233197(v=vs.110).aspx – 2012-07-27 22:35:09

+2

只是爲了添加 - 如果你不做互操作,使用'Option't'會更習慣使用 – 2012-07-27 22:44:10

+0

John - 是的,我提到無法使用Option。因此,幾乎所有暴露給C#的類都需要'[]'作爲C#開發人員期望的工作...... :( – 2012-07-27 22:45:12

14

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#類型的對象設置爲F#無效的可能性 - 這是兩全其美。

+3

我曾在一個大型項目中使用過這種方法,但最終後悔了它。如果一個類型可以爲null(無論是在F#中還是由於互操作),最好是顯式的。最好是定義一個空值檢查活動模式或'匹配空值 - > ...'的值?最後,這樣的變通辦法會讓人覺得不舒服,對於互操作,只需在你的類型中添加'[]'擁抱null。 – Daniel 2012-07-28 03:14:58

+0

@Daniel:我明白了壓力,OP只是想從C#中使用F#類型,所以我不知道有多少真正的互操作正在進行... – ildjarn 2012-07-28 03:25:03

+0

上面的isNull定義產生了語法錯誤:'顯式類型參數只能用於模塊或成員綁定'' 我試過這個:'let isNull(x:obj)= obj.ReferenceEquals(x,Unchecked.defaultof <_>)'' 當然,它會接受自參數綁定自動以來的任何值將其上拋向對象(隨之而來的盒子努力)。因此,非參考值的編譯器警告將丟失。 – George 2014-08-16 00:26:28

相關問題