2016-06-08 137 views
1

我一直在試圖重寫LINQ中的一些命令式代碼,直到我意識到我錯過了什麼。但我不知道爲什麼這是一個問題。考慮以下。測試失敗運行Test.Test2(testList)linq過濾器在默認情況下不能出現?

序列中沒有匹配的元素

在過去,我曾治療過這些形式可以互換,通過推我的謂詞的FirstSingle等編寫代碼較小..條款。很明顯,當涉及DeafultIfEmpty時,我不能這麼做。這是因爲使用WhereFirst,Single等是不可互換的嗎?或者,是因爲DefaultIfEmpty條款引入了複雜性?

編輯1 我添加了一個測試,顯示FirstOrDefault不起作用。它失敗,「未找到」不等於(空)。

public static class Test 
{ 
    public static string Test1(params string[] input) 
    { 
     return input 
      .Where(x => x == "apples") 
      .DefaultIfEmpty("bannanas") 
      .First(); 
    } 

    public static string Test2(params string[] input) 
    { 
     return input 
      .DefaultIfEmpty("bannanas") 
      .First(x => x == "apples"); 
    } 

    public static string Test3(params string[] input) 
    { 
     return input 
      .DefaultIfEmpty("bannanas") 
      .FirstOrDefault(x => x == "apples"); 
    } 
} 

public class TestStuff 
{ 
    [Fact] 
    public static void TestOneAndTwo() 
    { 
     var testList = new string[] { "oranges", "pears", "pineapples" }; 
     var one = Test.Test1(testList); 
     var two = Test.Test2(testList); 
     Assert.Equal(one, two); 
    } 

    [Fact] 
    public static void TestOneAndThree() 
    { 
     var testList = new string[] { "oranges", "pears", "pineapples" }; 
     var one = Test.Test1(testList); 
     var three = Test.Test3(testList); 
     Assert.Equal(one, three); 
    } 
} 

回答

2

的LINQ方法的順序是非常重要的,考慮一下每個方法確實給輸入枚舉({ "oranges", "pears", "pineapples" }):

public static string Test1(params string[] input) 
{ 
    return input 
     .Where(x => x == "apples") // empty enumerable, because no item matches "apples" 
     .DefaultIfEmpty("not found") // {"not found"}, since the enumerable is empty 
     .First(); //"not found", since we have this item 
} 

public static string Test2(params string[] input) 
{ 
    return input 
     .DefaultIfEmpty("not found") // { "oranges", "pears", "pineapples" } 
            //i.e., nothing changes, because input is not empty 
     .First(x => x == "apples"); //Exception because there is no 
            //item that is equal to "apples" 
} 

如果更改First的最後選擇FirstOrDefault,會產生null因爲default(string)null

+0

這對我來說很有意義,我沒有考慮過操作順序的重要性......並且我認爲DefaultIfEmpty爲後面的操作員設置了一個默認值,如果它們什麼都不返回的話。它的行爲如你所描述的那樣。 – cocogorilla

1

你應該使用FirstOrDefault(),這樣當陣列沒有任何匹配,則返回默認值,而不是拋出異常。


編輯:

Test1,所述Where子句後,所得到的序列變空。當應用DefaultIfEmpty時,結果序列將包含單個元素"not found"

Test2中,當應用DefaultIfEmpty時,該序列尚未過濾。因此,傳遞相同的序列。但是,First(predicate)試圖減少序列時,沒有什麼可以出來,這就是爲什麼InvalidOperationException發生。

+0

這並不值得。當我使用first或default的時候,我的「DefaultIfEmpty」被忽略,函數返回null。 – cocogorilla

+0

@cocogorilla查看我的編輯 – Xiaoy312

0

我同意Xiaoy312。 FirstOrDefault方法更好。然後檢查它是否爲null。

public static string Test2(params string[] input) 
{ 
    return input.FirstOrDefault(x => x == "apples") 
} 
+0

這個問題不是什麼架構會更好......這個例子是人爲設計的。問題是關於DefaultIfEmpty如何工作。雅各布的答案是勝利的,因爲他指出了「訂單問題」,這是我不明白的。 – cocogorilla