[英]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.