使用Default EqualityComparer进行Linq GroupBy的关键比较

时间:2023-01-24 16:11:33

I'm trying to do a Linq GroupBy on some objects using an explicit key type. I'm not passing an IEqualityComparer to the GroupBy, so according to the docs:

我正在尝试使用显式键类型对某些对象执行Linq GroupBy。我没有将IEqualityComparer传递给GroupBy,所以根据文档:

The default equality comparer Default is used to compare keys.

默认的相等比较器Default用于比较键。

It explains the EqualityComparer<T>.Default property like this:

它解释了EqualityComparer .Default属性,如下所示:

The Default property checks whether type T implements the System.IEquatable<T> generic interface and if so returns an EqualityComparer<T> that uses that implementation.

Default属性检查类型T是否实现System.IEquatable 泛型接口,如果是,则返回使用该实现的EqualityComparer

In the code below, I'm grouping an array of Fred objects. They have a key type called FredKey, which implements IEquatable<FredKey>.

在下面的代码中,我正在对Fred对象的数组进行分组。它们有一个名为FredKey的密钥类型,它实现了IEquatable

That should be enough to make the grouping work, but the grouping is not working. In the last line below I should have 2 groups, but I don't, I just have 3 groups containing the 3 input items.

这应该足以使分组工作,但分组不起作用。在下面的最后一行我应该有2组,但我没有,我只有3组包含3个输入项。

Why is the grouping not working?

为什么分组不起作用?

class Fred
{
    public string A;
    public string B;
    public FredKey Key
    {
        get { return new FredKey() { A = this.A }; }
    }
}

class FredKey : IEquatable<FredKey>
{
    public string A;
    public bool Equals(FredKey other)
    {
        return A == other.A;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var f = new Fred[]
        {
            new Fred {A = "hello", B = "frog"},
            new Fred {A = "jim", B = "jog"},
            new Fred {A = "hello", B = "bog"},
        };

        var groups = f.GroupBy(x => x.Key);
        Debug.Assert(groups.Count() == 2);  // <--- fails
    }
}

2 个解决方案

#1


16  

From MSDN

来自MSDN

If you implement IEquatable, you should also override the base class implementations of Object::Equals(Object) and GetHashCode() so that their behavior is consistent with that of the IEquatable::Equals method. If you do override Object::Equals(Object), your overridden implementation is also called in calls to the static Equals(System.Object, System.Object) method on your class. This ensures that all invocations of the Equals() method return consistent results.

如果实现IEquatable,还应该覆盖Object :: Equals(Object)和GetHashCode()的基类实现,以便它们的行为与IEquatable :: Equals方法的行为一致。如果您重写Object :: Equals(Object),则在对类的静态Equals(System.Object,System.Object)方法的调用中也会调用重写的实现。这可确保Equals()方法的所有调用都返回一致的结果。

add this to FredKey and it should work

将此添加到FredKey,它应该工作

public override int GetHashCode()
    {
        return A.GetHashCode();
    }

#2


5  

Here is a complete example with a Fiddle. Note: the example differs slightly from the question's example.

这是一个完整的小提琴示例。注意:该示例与问题示例略有不同。

The following implementation of IEquatable can act as the TKey in GroupBy. Note that it includes both GetHashCode and Equals.

IEquatable的以下实现可以充当GroupBy中的TKey。请注意,它包括GetHashCode和Equals。

public class CustomKey : IEquatable<CustomKey>
{
    public string A { get; set; }
    public string B { get; set; }

    public bool Equals(CustomKey other)
    {
        return other.A == A && other.B == B;
    }

    public override int GetHashCode()
    {
        return string.Format("{0}{1}", A, B).GetHashCode();
    }
}

public class Custom
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
}

public static void Main()
{
    var c = new Custom[]
       {
           new Custom {A = "hello", B = "frog" },
           new Custom {A = "jim", B = "jog" },
           new Custom {A = "hello", B = "frog" },
       };

    var groups = c.GroupBy(x => new CustomKey { A = x.A, B = x.B } );
       Console.WriteLine(groups.Count() == 2);  
}

#1


16  

From MSDN

来自MSDN

If you implement IEquatable, you should also override the base class implementations of Object::Equals(Object) and GetHashCode() so that their behavior is consistent with that of the IEquatable::Equals method. If you do override Object::Equals(Object), your overridden implementation is also called in calls to the static Equals(System.Object, System.Object) method on your class. This ensures that all invocations of the Equals() method return consistent results.

如果实现IEquatable,还应该覆盖Object :: Equals(Object)和GetHashCode()的基类实现,以便它们的行为与IEquatable :: Equals方法的行为一致。如果您重写Object :: Equals(Object),则在对类的静态Equals(System.Object,System.Object)方法的调用中也会调用重写的实现。这可确保Equals()方法的所有调用都返回一致的结果。

add this to FredKey and it should work

将此添加到FredKey,它应该工作

public override int GetHashCode()
    {
        return A.GetHashCode();
    }

#2


5  

Here is a complete example with a Fiddle. Note: the example differs slightly from the question's example.

这是一个完整的小提琴示例。注意:该示例与问题示例略有不同。

The following implementation of IEquatable can act as the TKey in GroupBy. Note that it includes both GetHashCode and Equals.

IEquatable的以下实现可以充当GroupBy中的TKey。请注意,它包括GetHashCode和Equals。

public class CustomKey : IEquatable<CustomKey>
{
    public string A { get; set; }
    public string B { get; set; }

    public bool Equals(CustomKey other)
    {
        return other.A == A && other.B == B;
    }

    public override int GetHashCode()
    {
        return string.Format("{0}{1}", A, B).GetHashCode();
    }
}

public class Custom
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
}

public static void Main()
{
    var c = new Custom[]
       {
           new Custom {A = "hello", B = "frog" },
           new Custom {A = "jim", B = "jog" },
           new Custom {A = "hello", B = "frog" },
       };

    var groups = c.GroupBy(x => new CustomKey { A = x.A, B = x.B } );
       Console.WriteLine(groups.Count() == 2);  
}