2011-11-22 110 views
5

我有這樣的LINQ到對象查詢:LINQ到對象時,對象爲空VS的LINQ to SQL

var result = Users.Where(u => u.Address.Country.Code == 12) 

我得到一個異常,如果地址或國家爲空。
爲什麼這個查詢不檢查地址是否爲空,並且在那個之後被觸發? 這樣,我就不用寫這個可怕的查詢:

var result = Users.Where(u => u.Address != null && 
           u.Address.Country != null && 
           u.Address.Country.Code == 12) 

在LINQ to SQL中的第一個查詢wiil做的工作(當然從其他原因)。

是一種避免linq中的「空檢查」對象?

回答

6

不幸的是,「null」在C#(以及許多其他編程語言)中處理不一致。可空算術解除。也就是說,如果你對可爲空的整數進行算術運算,並且沒有一個操作數爲空,那麼你得到的是正常的答案,但是如果它們中的任何一個都爲null,你就會得到空值。但是「會員訪問」運營商是而不是解除了;如果你給成員訪問一個空操作數「。」運算符,它會拋出異常而不是返回空值。

是我們從頭開始設計一個類型的系統,我們可以說,所有類型都是空的,而且類型無關的操作數的包含空操作任何表達生成一個空的結果。因此,使用空接收方或空參數調用方法會產生空結果。這個系統有很大的意義,但顯然現在對我們來說實在太遲了;已編寫數百萬行代碼,預計當前行爲。

我們已經考慮添加一個「提升」成員訪問操作符,可能標註爲.?。所以你可以說where user.?Address.?Country.?Code == 12,那會產生一個可爲空的int,然後可以將其與12作爲可空的整數進行比較。然而,這從來沒有超過設計過程中的「是的,這在未來的版本中可能會很好」,所以我不會期待它很快。


更新:上述「貓王」運算符在C#6.0中實現。

+0

現在它以語言實現(如果語法上相反),但在LINQ SQL翻譯中沒有作爲無操作實現,因此無法輕鬆使用。 – NetMage

5

不,它是一個空引用異常,就像訪問var x = u.Address.Country.Code;將是一個NullReferenceException

您必須始終確保LINQ to object中的對象不是null,與其他任何代碼語句一樣。

你可以做到這一點無論使用您有&&邏輯,或者你可以鏈Where條款以及(儘管這會包含更多的迭代器,可能執行更慢):

var result = Users.Where(u => u.Address != null) 
        .Where(u.Address.Country != null) 
        .Where(u.Address.Country.Code == 12); 

注意:本Maybe()下面的方法只是作爲一種「你也可以」的方法提供,我並不是說它是好的或壞的,只是顯示一些人做的事情。如果你不喜歡Maybe()我不重複投票如果你不喜歡我只是重複我見過的各種解決方案...

我見過一些Maybe()擴展方法的書面,讓你做這種事情你要。有些人喜歡/不喜歡這些,因爲它們是在null引用上運行的擴展方法。我不是說好或壞,只是有些人認爲這違反了良好的面向對象的行爲。

例如,您可以創建類似的擴展方法:

public static class ObjectExtensions 
{ 
    // returns default if LHS is null 
    public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator) 
     where TInput : class 
    { 
     return (value != null) ? evaluator(value) : default(TResult); 
    } 

    // returns specified value if LHS is null 
    public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator, TResult failureValue) 
     where TInput : class 
    { 
     return (value != null) ? evaluator(value) : failureValue; 
    } 
} 

然後執行:

var result = Users.Where(u => u.Maybe(x => x.Address) 
        .Maybe(x => x.Country) 
        .Maybe(x => x.Code) == 12); 

從本質上講,這只是級聯的null環比回落(或默認值的情況下的非參考類型)。

UPDATE

如果您想提供一個非缺省值失敗值(說代碼爲-1,如果任何部分爲空),你只希望通過新的故障值到也許( ):

// if you wanted to compare to zero, for example, but didn't want null 
// to translate to zero, change the default in the final maybe to -1 
var result = Users.Where(u => u.Maybe(x => x.Address) 
        .Maybe(x => x.Country) 
        .Maybe(x => x.Code, -1) == 0); 

就像我說的,這只是衆多解決方案之一。有些人不喜歡能夠調用null參考類型的擴展方法,但有些人傾向於使用這種方法來解決這些級聯問題。

但是,目前,C#中沒有內置的無引號安全操作符,因此您要麼像以前一樣使用條件空值檢查,將您的Where()語句鏈接起來,以便它們會過濾出null,或者建立一些東西讓你像上面的Maybe()方法那樣級聯null

+0

請注意,如果將'Code'與'0'進行比較,即使任何父屬性爲'null'(因爲該類型的默認值爲「0」),它也會返回'true'。 –

+0

@GeorgeDuckett:是的,的確如此。這只是許多重載之一,我實際上在我用來提供其他默認值的庫中有幾個重載。例如....(見更新的代碼)。 –

+0

我真的不喜歡那些可能,閱讀代碼時難以理解,代碼幾乎相同。 – Magnus