Как работает GroupBy

129
24 января 2021, 16:00

Всем доброго времени суток! Прошу помочь разобраться в том, как работает GroupBy и в чем разница приведенного ниже кода.

class Employee {
    public string Name { get; set; }
    public string Id { get; set; }
    public string OfficeId { get; set; }
}
class Person {
    public string Name { get; set; }
    public string Id { get; set; }
}
class Program {
    static void Main(string[] args) {
        List<Employee> Employee = new List<Employee>() {
            new Employee { Id = "1", Name = "Alex", OfficeId = "1" },
            new Employee { Id = "2", Name = "John", OfficeId = "1" },
            new Employee { Id = "1", Name = "Alex", OfficeId = "2" },
            new Employee { Id = "2", Name = "John", OfficeId = "2" },
            new Employee { Id = "2", Name = "John", OfficeId = "3" },
        };
        var result1 = Employee.GroupBy(p => new { p.Id, p.Name });
        var result2 = Employee.GroupBy(p => new Person { Id = p.Id, Name = p.Name });
    }
}

В случае с result1 создается коллекция с подобным типом IEnumerable<IGrouping<AnonymousType<string, string>, Employee>> где имеются вде записи с ключами Id и Name. Элемент, где Id = "1", Name = "Alex" имеет две записи, элемент, где Id = "2", Name = "John" имеет соответственно три записи.

Здесь у меня особо вопросов нет и структура объекта такая, какой я и ожидал ее увидеть, но мне понадобилось данный объект передать в метод в качестве аргумента и я создал класс Person, чтобы он заменял анонимный тип.

В result2 создается коллекция примерно вот такая коллекция IEnumerable<IGrouping<Person, Employee>> и я могу явно указать тип, который метод должен принимать. Проблема в том, что тут создался список с пятью элементами (ключами) и каждый элемент имеет по одному значению, чего я не ожидал и не могу понять почему так происходит.

Буду благодарен, если объяснят почему идет такая разница результатов, если вместо анонимного типа в GroupBy указать пользовательский тип и как сделать, чтобы получить объект с структурой данных как в result1, но с указанием пользовательского типа.

P.S. Если в GroupBy указать группировку по одному полю (например Id), то результат будет, что и в result1 только ключ будет состоять из строки, но как быть когда ключ должен представлять из себя объект с несколькими свойствами?

Answer 1

Для анонимных типов компилятор автоматически генерирует методы Equals и GetHashCode, которые:

определяются через методы Equals и GetHashCode свойств, два экземпляра одного и того же анонимного типа равны, только если равны их свойства.

цитата отсюда: Анонимные типы (Руководство по программированию в C#)

Поэтому следующий код выведет True:

var x = new { Id = 1, Name = "Ivan" };
var y = new { Id = 1, Name = "Ivan" };
Console.WriteLine(x.Equals(y));

В классе же Person метод Equals не переопределен, поэтому используется реализация из базового класса Object, которая сравнивает ссылки.

Ссылки на разные экземпляры, конечно, не равны, поэтому такой код вернет False:

var x = new Person { Id = 1, Name = "Ivan" };
var y = new Person { Id = 1, Name = "Ivan" };
Console.WriteLine(x.Equals(y));

Таким образом, группировка во втором случае создаст отдельную группу для каждого экземпляра Person.

READ ALSO
Ошибка System.UnauthorizedAccessException" в mscorlib.dll в приложении в win 10

Ошибка System.UnauthorizedAccessException" в mscorlib.dll в приложении в win 10

Есть мое приложениекоторое работает, и вот в него добавлю механизм записи технической информации

119
Как отключить полосы прокрутки у FlowDocumentScrollViewer в WPF?

Как отключить полосы прокрутки у FlowDocumentScrollViewer в WPF?

Как отключить полосы прокрутки у FlowDocumentScrollViewer в WPF?

95
Как отсортировать двусвязный список SplDoublyLinkedList?

Как отсортировать двусвязный список SplDoublyLinkedList?

Есть двусвязный список который мне нужно отсортировать за O(n Log n) времяЯ бы хотел использовать merge sort, но я никак не могу сообразить, как это...

121