簡體   English   中英

為什么 Linq 加入 Nullable<T> 與== 不一樣?

[英]Why does Linq join on Nullable<T> not work the same as ==?

var list1 = new List<(string Foo, DayOfWeek? DayOfWeek)> {("Friday", DayOfWeek.Friday), ("Null", null)};
var list2 = new List<(int Num, DayOfWeek? DayOfWeek)> {(1, DayOfWeek.Friday), (2, DayOfWeek.Friday), (3, null)};

Console.WriteLine("Q1:");
var q1 = from l1 in list1
         from l2 in list2
         where l1.DayOfWeek == l2.DayOfWeek
         group l2.Num by l1.Foo
         into g
         select new {g.Key, Sum = g.Sum()};

foreach (var x in q1)
{
    Console.WriteLine($"{x.Key} = {x.Sum}");
}

Console.WriteLine("Q2:");
var q2 = from l1 in list1
         join l2 in list2 on l1.DayOfWeek equals l2.DayOfWeek
         group l2.Num by l1.Foo
         into g
         select new {g.Key, Sum = g.Sum()};

foreach (var x in q2)
{
    Console.WriteLine($"{x.Key} = {x.Sum}");
}

輸出:

Q1:
Friday = 3
Null = 3
Q2:
Friday = 3

請注意,Q2 沒有“Null”的值!

這告訴我的是 Linq equals / .Join()不使用==.Equals()評估。 那么,它在使用什么,當值為null時,一個Nullable<T>不等於另一個Nullable<T>是否有合理的理由?

Nullable.Equals(Object)HasValue屬性為 false 時返回true ,另一個參數為null 看來,第一個查詢被正確評估。

至於第二個查詢,我已將其更改為

var q2 = from l1 in list1
    join l2 in list2 on new { l1.DayOfWeek } equals new { l2.DayOfWeek }
    group l2.Num by l1.Foo
    into g
    select new { g.Key, Sum = g.Sum() };

第一次查詢得到相同的結果。

根據MSDN 的說法,這樣做的原因是Enumerable.Join方法在 SQL 中用作inner join聯接

如果第一個集合中的元素沒有匹配的元素,則它不會出現在結果集中。 Join 方法由 C# 中的 join 子句調用,實現了內部聯接。

根據消息來源,創建查找時會省略空鍵。

internal static Lookup<TKey, TElement> CreateForJoin(IEnumerable<TElement> source, Func<TElement, TKey> keySelector, IEqualityComparer<TKey> comparer) {
            Lookup<TKey, TElement> lookup = new Lookup<TKey, TElement>(comparer);
            foreach (TElement item in source) {
                TKey key = keySelector(item);
                if (key != null) lookup.GetGrouping(key, true).Add(item);
            }
            return lookup;
        }

因此,與對象相等性無關, null值只是在join期間被跳過。 我創建了一個自定義的EqualityComparer ,將它傳遞給Join方法並且沒有觀察到任何傳入Equals方法的null值。

如果您嘗試使用外連接組連接,並編寫如下內容

var q3 = list1.GroupJoin(list2, _ => _.DayOfWeek, _ => _.DayOfWeek, (l1, list) =>
    new
    {
        Name = l1.Foo,
        Num = list.Sum(_ => _.Num)
    }
);

或這個

var q3 = from l1 in list1
    join l2 in list2 on l1.DayOfWeek equals l2.DayOfWeek into weeks
    from week in weeks.DefaultIfEmpty()
    group week.Num by l1.Foo
    into g
    select new { g.Key, Sum = g.Sum() };

你會得到以下輸出(正確的行數,但不正確的總和)

Q3:
Friday = 3
Null = 0

因為在外連接和分組期間Num值丟失(丟失的鍵被默認值替換)。 看來,當您使用沒有join where子句或使用上面的匿名類型的變通方法時,它似乎才有效。

當鍵是匿名類型時,join 中的相等性被正確評估,因為所有匿名對象實例都不為空,在相等性期間創建並由DayOfWeek?進行比較DayOfWeek? 實例內的屬性(不是實例本身),根據此線程

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM