IEqualityComparer не работает должным образом
У меня есть List
путей файлов, хранящихся на моем компьютере. Моя цель - сначала отфильтровать файлы с таким же именем и затем отфильтровать те, которые имеют одинаковый размер.
Для этого я создал два класса, реализующих IEqualityComparer<string>
, и реализовал методы Equals
и GetHashCode
.
var query = FilesList.Distinct(new CustomTextComparer())
.Distinct(new CustomSizeComparer());
Код для обоих классов приведен ниже: -
public class CustomTextComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
if (Path.GetFileName(x) == Path.GetFileName(y))
{
return true;
}
return false;
}
public int GetHashCode(string obj)
{
return obj.GetHashCode();
}
}
public class CustomSizeComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
if (new FileInfo(x).Length == new FileInfo(y).Length)
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(string obj)
{
return obj.GetHashCode();
}
}
Но код не работает.
Он не бросает никаких исключений и не имеет ошибки компилятора, но проблема в том, что код не работает (не исключает дублирование файлов).
Итак, как я могу исправить эту проблему? Я могу что-то сделать, чтобы код работал правильно.
Ответы
Ответ 1
Измените свой GetHashCode
для работы с сравниваемым значением. То есть для вашего компаратора размера:
public int GetHashCode(string obj)
{
return FileInfo(x).Length.GetHashCode();
}
А для другого:
public int GetHashCode(string obj)
{
return Path.GetFileName(obj).GetHashCode();
}
В соответствии с этим ответом - Какова роль GetHashCode в IEqualityComparer <T> в .NET? сначала оценивается хеш-код. Equals
вызывается в случае столкновения.
Очевидно, было бы разумно работать с FileInfo
s, а не на строках.
Итак, возможно:
FileList.Select(x => new FileInfo(x))
.Distinct(new CustomTextComparer())
.Distinct(new CustomSizeComparer());
Конечно, тогда вам нужно изменить свои компаньоны для работы над правильным типом.
Ответ 2
Ваш GetHashCode должен возвращать одно и то же значение для любых одинаковых значений:
// Try this
public int GetHashCode(string obj)
{
return Path.GetFileName(x).GetHashCode();
}
// And this
public int GetHashCode(string obj)
{
return new FileInfo(x).Length.GetHashCode();
}
Но это намного проще для всей задачи без дополнительных классов:
var query = FilesList
.GroupBy(f => Path.GetFileName(f)).Select(g => g.First())
.GroupBy(f => new FileInfo(f).Length).Select(g => g.First())
.ToList();
Ответ 3
Хэш-код используется до вызова Equals. Поскольку ваш код дает разные хеш-коды для элементов, которые равны, вы не получаете желаемого результата. Вместо этого вы должны убедиться, что возвращаемый хэш-код равен, когда элементы равны, например:
public class CustomTextComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
if (Path.GetFileName(x) == Path.GetFileName(y))
{
return true;
}
return false;
}
public int GetHashCode(string obj)
{
return Path.GetFileName(obj).GetHashCode();
}
}
Однако, как отметил Петр, это не совсем хороший способ достичь вашей цели, так как вы будете делать много Path.GetFileName
и new FileInfo
соответственно, что будет значительный успех, особенно потому, что вы имеете дело с файловой системой, которая не точно известна своей скоростью ответа.