Я пытаюсь написать запрос Linq, который возвращает массив объектов с уникальными значениями в своих конструкторах. Для целых типов Distinct возвращает только одну копию каждого значения, но когда я пытаюсь создать список объектов, все разваливается. Я подозреваю, что это проблема с оператором равенства для моего класса, но когда я устанавливаю точку останова, она никогда не попадает.Distinct() возвращает дубликаты с пользовательским типом
Фильтрация дубликата int в подвыражении решает проблему, а также избавляет меня от создания объектов, которые будут немедленно удалены, но мне любопытно, почему эта версия не работает.
UPDATE: 11:04 PM Несколько человек указали, что MyType не переопределяет GetHashCode(). Боюсь, я упростил пример. Первоначальный MyType действительно реализует его. Я добавил его ниже, измененный только для того, чтобы поместить хеш-код в временную переменную, прежде чем возвращать его.
Запуск через отладчик, я вижу, что все пять вызовов GetHashCode возвращают другое значение. И поскольку MyType наследует только объект Object, это, по-видимому, такое же поведение, что и объект Object.
Должен ли я быть правильным, чтобы сделать вывод о том, что хеш должен основываться на содержании Value? Это была моя первая попытка переопределить операторов, и в то время не показалось, что GetHashCode должен быть особенно причудливым. (Это первый раз, когда один из моих проверок равенства, похоже, не работает должным образом.)
class Program
{
static void Main(string[] args)
{
int[] list = { 1, 3, 4, 4, 5 };
int[] list2 =
(from value in list
select value).Distinct().ToArray(); // One copy of each value.
MyType[] distinct =
(from value in list
select new MyType(value)).Distinct().ToArray(); // Two objects created with 4.
Array.ForEach(distinct, value => Console.WriteLine(value));
}
}
class MyType
{
public int Value { get; private set; }
public MyType(int arg)
{
Value = arg;
}
public override int GetHashCode()
{
int retval = base.GetHashCode();
return retval;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
MyType rhs = obj as MyType;
if ((Object)rhs == null)
return false;
return this == rhs;
}
public static bool operator ==(MyType lhs, MyType rhs)
{
bool result;
if ((Object)lhs != null && (Object)rhs != null)
result = lhs.Value == rhs.Value;
else
result = (Object)lhs == (Object)rhs;
return result;
}
public static bool operator !=(MyType lhs, MyType rhs)
{
return !(lhs == rhs);
}
}
Внедрили ли вы GetHashCode()? Похоже, вы могли бы вернуть Value.HashCode() – cordialgerm
В основном вы используете тип значения в типе ссылки MyClass. В этом нет ничего плохого, но вам нужно подумать о том, что значение MyClass является идентификатором экземпляра, а не местоположением объекта в памяти, являющимся его личностью (по умолчанию для ссылочных типов). Таким образом, переопределите GetHashCode(), чтобы вернуть Value.GetHashCode(), чтобы отразить эту идентичность. – dthorpe
@dthorpe - Я думаю, что моей главной проблемой было неспособность распознать, как GetHashCode, вероятно, будет реализован в Object. Мое мышление в то время было по существу: «Мне не нужно ничего фантастического, реализация по умолчанию должна быть достаточно хорошей». Я не буду повторять эту ошибку. В следующий раз, когда я перейду на переопределение оператора ==, я найду совершенно другой способ испортить его. – ThatBlairGuy