Как .NET определяет равные объекты в LINQ «выберите новый»? - PullRequest
2 голосов
/ 31 июля 2011

Вот пример:

class A
{ 
    public long ParentId; 
    public string ParentName; 
    public string Name; 
}

// list is IEnumerable<A>
var selected = list
    .Select(x => new {
        parent = new {
            id = x.ParentId,
            name = x.ParentName
        }
        name = x.Name
    });
var grouped = selected.GroupBy(x => x.parent);

Итак, поскольку группировка успешно завершена, я делаю вывод, что parent не создается для двух разных сущностей, если оба они имеют одинаковые ParentId и ParentName. Другими словами, если list[i].ParentId == list[j].ParentId и list[i].ParentName == list[j].ParentName, то после выбора selected[i].parent == selected[j].parent.
Я прав? Я думал, что new создает новый объект на каждой итерации в исходной коллекции. Как .NET это делает?

Ответы [ 5 ]

4 голосов
/ 31 июля 2011

Вопрос в том, как Равенство продвигается.Для анонимных классов возвращается значение true, если и только если свойства совпадают, и для каждого из свойств существует равенство.

Сравните эти два значения:

Console.WriteLine(new { A = 1, B = "a" } == new { A = 1, B = "a" });  //false
Console.WriteLine(new { A = 1, B = "a" }.Equals(new { A = 1, B = "a" }));  //true
1 голос
/ 31 июля 2011

GroupBy использует стандартный подход hash + equals (общий с Hashtable, Dictionary<,> и т. Д.), То есть:

  • GetHashCode определяет определенное неравенство ((если отличается) и возможно равенство (когда одинаковое)
  • Равен (IEquatable<T>.Equals или object.Equals) определяет равенство

Ваша группа по проекции сравнивает ParentName, так что используется при сравнении предметов.Так как string имеет четкие определения GetHashCode / Equals, это прекрасно работает.

Ваш последний вопрос:

Я думал, что new создает новый объект на каждой итерациичерез коллекцию источников.

Строго, да.Но он перечисляет его только один раз, так что это не проблема.Даже если это не так, сам анонимный тип new {...} сам имеет определение равенства, основанное на элементах компонента.

0 голосов
/ 31 июля 2011

Хорошо, чтобы переопределить поведение сравнения по умолчанию .Net, вы должны сделать что-то вроде этого:

class A
{ 
    public long ParentId; 
    public string ParentName; 
    public string Name; 


    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (obj.GetType() != typeof(A))
            return false;
        var other=(A) obj;
        return Equals(other.ParentId, ParentId) && Equals(other.ParentName, ParentName);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (ParentName.GetHashCode() * 397) ^ ParentId.GetHashCode();
        }
    }
}
0 голосов
/ 31 июля 2011

Каждый объект .Net реализует интерфейс IComparer и имеет собственную реализацию метода CompareTo..Net просто использует этот метод, чтобы определить, является ли что-то равным, в этом случае .net просто проверяет, имеют ли открытые свойства обоих объектов одинаковые значения, следовательно, они равны.

РЕДАКТИРОВАТЬ: Извините, я путаю IComparer CompareTo сobject.Equals, каждый объект реализует метод Equals и, в качестве примера, класс String переопределяет этот метод и просто проверяет, что обе строки содержат одно и то же значение, не ссылаясь на один и тот же адрес памяти.

0 голосов
/ 31 июля 2011

Он не вызывает == оператора.

Он спрашивает EqualityComparer<T>.Default, равны ли экземпляры.В свою очередь, он вызывает Equals из IEquatable реализации, если есть, или метод Object.Equals в качестве последнего средства.

Реализация по умолчанию Equals поддерживает равенство ссылок для ссылочных типов и битовое равенство для типов значений.

Однако вы можете переопределить метод, если вам нужно настроитьваши собственные правила равенства.

В вашем примере parent, если анонимного типа.Компилятор C # генерирует полевые Equals и GetHashCode реализации, потому что, очевидно, вы не можете предоставить их сами.

...