Ключевые сравнения для Linq GroupBy с использованием стандартного EqualityComparer

Я пытаюсь сделать Linq GroupBy для некоторых объектов, используя явный тип ключа. Я не передаю IEqualityComparer в GroupBy, поэтому согласно документам:

Сравнение сравнения по умолчанию Default используется для сравнения ключей.

Он объясняет свойство EqualityComparer<T>.Default следующим образом:

Свойство Default проверяет, тип T реализует System.IEquatable<T> общий интерфейс и если это возвращает EqualityComparer<T>, который использует это реализация.

В приведенном ниже коде я группирую массив из объектов Fred. Они имеют ключевой тип, называемый FredKey, который реализует IEquatable<FredKey>.

Этого должно быть достаточно, чтобы сгруппировать работу, но группировка не работает. В последней строке ниже у меня должно быть 2 группы, но у меня нет, у меня всего 3 группы, содержащие 3 элемента ввода.

Почему группа не работает?

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
    }
}

Ответы

Ответ 1

От MSDN

Если вы реализуете IEquatable, вы также должны переопределить реализации базового класса Object:: Equals (Object) и GetHashCode(), чтобы их поведение соответствовало их методу метода IEquatable:: Equals. Если вы переопределите Object:: Equals (Object), ваша переопределенная реализация также вызывается при вызове метода статического метода Equals (System.Object, System.Object) в вашем классе. Это гарантирует, что все вызовы метода Equals() возвращают согласованные результаты.

добавьте это в FredKey, и он должен работать

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

Ответ 2

Вот полный пример с помощью скрипта. Примечание: пример немного отличается от примера вопроса.

Следующая реализация IEquatable может действовать как TKey в GroupBy. Обратите внимание, что он включает в себя как 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);  
}