c# - LINQ 加入可空键

标签 c# linq

LINQ Join()使用 Nullable<int> 的方法对于 TKey跳过空键匹配。我在文档中缺少什么?我知道我可以切换到 SelectMany() ,我很好奇为什么这个相等操作像 SQL 而不是像 C#,因为据我所知,EqualityComparer<int?>.Default对于空值,它的工作方式与我期望的完全一样。

http://msdn.microsoft.com/en-us/library/bb534675.aspx

using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;

public class dt
{
   public int? Id;
   public string Data;
}

public class JoinTest
{
    public static int Main(string [] args)
    {
        var a = new List<dt>
        {
            new dt { Id = null, Data = "null" },
            new dt { Id = 1, Data = "1" },
            new dt { Id = 2, Data = "2" }
        };

        var b = new List<dt>
        {
            new dt { Id = null, Data = "NULL" },
            new dt { Id = 2, Data = "two" },
            new dt { Id = 3, Data = "three" }
        };

        //Join with null elements
        var c = a.Join( b,
            dtA => dtA.Id,
            dtB => dtB.Id,
            (dtA, dtB) => new { aData = dtA.Data, bData = dtB.Data } ).ToList();
        // Output:
        // 2 two
        foreach ( var aC in c )
            Console.WriteLine( aC.aData + " " + aC.bData );
        Console.WriteLine( " " );

        //Join with null elements converted to zero
        c = a.Join( b,
            dtA => dtA.Id.GetValueOrDefault(),
            dtB => dtB.Id.GetValueOrDefault(),
            (dtA, dtB) => new { aData = dtA.Data, bData = dtB.Data } ).ToList();

        // Output:
        // null NULL
        // 2 two
        foreach ( var aC in c )
            Console.WriteLine( aC.aData + " " + aC.bData );

        Console.WriteLine( EqualityComparer<int?>.Default.Equals( a[0].Id, b[0].Id ) );
        Console.WriteLine( EqualityComparer<object>.Default.Equals( a[0].Id, b[0].Id ) );
        Console.WriteLine( a[0].Id.Equals( b[0].Id ) );

        return 0;
    }
}

最佳答案

Enumerable.Join 使用 JoinIterator (私有(private)类)迭代匹配元素。 JoinIterator使用 Lookup<TKey, TElement> 用于创建序列键查找:

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 local in source)
    {
        TKey key = keySelector(local);
        if (key != null) // <--- Here
        {
            lookup.GetGrouping(key, true).Add(local);
        }
    }
    return lookup;
}

这里有趣的部分是跳过 null 的键.这就是为什么在不提供默认值的情况下您只有一个匹配项。


看来我找到了这种行为的原因。 Lookup 使用默认的 EqualityComparer,它将返回 0两者都是 null 的键和键是0 :

int? keyA = 0;
var comparer = EqualityComparer<int?>.Default;
int hashA = comparer.GetHashCode(keyA) & 0x7fffffff; // from Lookup class
int? keyB = null;
int hashB = comparer.GetHashCode(keyB) & 0x7fffffff;
Console.WriteLine(hashA); // 0
Console.WriteLine(hashB); // 0

可能跳过空值以避免匹配 null0键。

关于c# - LINQ 加入可空键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17092435/

相关文章:

c# - 检查项目 ID 与选定的 ID

c# - 如何将列表项添加到在代码隐藏中生成的下拉列表?

c# - 当 '\' 后跟成为有效转义序列的字符时,在 '\' 字符上拆分字符串

c# - Linq 扩展方法和错误处理

c# - 寻找C#中最无痛的非RDBMS存储方式

c# - WPF 应用程序文件关联 : DefaultIcon is not working

c# - Java/C# 类中实例变量的使用

c# - 使用 LINQ 而不是使用 for-each 循环

c# - 在 Linq 中动态选择列和聚合函数

c# - LINQ 按某种规则将 List<IEnumerable<T>> 合并为一个 IEnumerable<T>