C# 为什么可空上的Linq连接与==的工作原理不同?

C# 为什么可空上的Linq连接与==的工作原理不同?,c#,linq,nullable,C#,Linq,Nullable,请注意,Q2没有空值 这告诉我Linq equals/.Join不使用==或.equals求值。那么,它在使用什么呢?当值为null时,一个null值不等于另一个null值有什么合理的原因吗?当HasValue属性为false时返回true,而另一个参数为null。看起来,第一个查询的计算结果是正确的 至于第二个问题,我把它改为 var q2=来自列表1中的l1 在新的{l1.DayOfWeek}上加入列表2中的l2等于新的{l2.DayOfWeek} 按l1.Foo分组l2.Num 进入g 选

请注意,Q2没有空值

这告诉我Linq equals/.Join不使用==或.equals求值。那么,它在使用什么呢?当值为null时,一个null值不等于另一个null值有什么合理的原因吗?

当HasValue属性为false时返回true,而另一个参数为null。看起来,第一个查询的计算结果是正确的

至于第二个问题,我把它改为

var q2=来自列表1中的l1 在新的{l1.DayOfWeek}上加入列表2中的l2等于新的{l2.DayOfWeek} 按l1.Foo分组l2.Num 进入g 选择新的{g.Key,Sum=g.Sum}; 并得到与第一次查询相同的结果

这是因为该方法在SQL中充当内部联接,根据

如果第一个集合中的元素没有匹配的元素,则 不会出现在结果集中。由调用的联接方法 C中的join子句实现内部联接

根据,在创建查找时,将省略空键

内部静态查找CreateForJoinIEnumerable源、Func键选择器、IEqualityComparer比较器{ Lookup Lookup=新的Lookupcomparer; 源中的每个远程服务项{ TKey=按键选择项; 如果key!=null lookup.GetGroupingkey,则为true.Additem; } 返回查找; } 因此,与对象相等性无关,在连接期间跳过空值。我创建了一个自定义EqualityComparer,将其传递给Join方法,并且没有观察到Equals方法中出现任何空值

如果你试着用或,写下这样的东西

var q3=list1.GroupJoinlist2,=>\uu0.DayOfWeek,0=>\u0.DayOfWeek,l1,list=> 刚出现的 { Name=l1.Foo, Num=list.Sum\u=>\ unum } ; 还是这个

var q3=来自列表1中的l1 在l1.DayOfWeek上将l2加入列表2中等于l2.DayOfWeek,以周为单位 从周中的周。DefaultIfEmpty 分组周。按l1.Foo的Num 进入g 选择新的{g.Key,Sum=g.Sum}; 您将获得以下输出正确的行数,但总和不正确

问题3: 星期五=3 Null=0 因为Num值在外部联接和分组期间丢失,丢失的键被默认值替换。看起来,只有当您使用where子句而不使用join或使用上面的匿名类型的变通方法时,它才起作用


当键为匿名类型时,联接中的相等性将正确计算,因为所有匿名对象实例都不是空的,都是在相等期间创建的,并由DayOfWeek进行比较?属性,而不是实例本身,根据这一点,

看起来像LINQ中的equals to Objects使用三值逻辑,就像SQL一样,其中NULL不等于任何值,甚至不等于任何值NULL@PanagiotisKanavos是的,看起来确实是这样。这是一个bug,还是出于设计?如果按照设计,为什么这是有意义的,当在C世界的其他地方,null等于null?而不是使用null,使用Count>0。该属性仍然存在,但为空。变通方法为+1。但这只是放大了我的问题。为什么直接平等比较不起作用,而匿名类型比较起作用?@ShaulsaysISupportMonica请看最新的答案,我在研究中添加了很多细节:哇,这是一些非常彻底的研究。谢谢我仍然不明白为什么这是故意的行为,但无论如何,你应该为此得到赞扬-
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