2014-01-28 5 views
10

У меня есть List путей файлов, хранящихся на моем компьютере. Моя цель - сначала отфильтровать файлы с тем же именем и затем отфильтровать те, которые имеют одинаковый размер.
Для этого я сделал два класса, реализующих IEqualityComparer<string>, и реализовал методы Equals и GetHashCode.IEqualityComparer не работает должным образом

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(); 
    } 
} 

Но код не работает.

Он не выдает никаких исключений и не имеет ошибки компилятора, но проблема в том, что код не работает (не исключает дублирование файлов).

Итак, как я могу исправить эту проблему? Я могу что-то сделать, чтобы код работал правильно.

+0

Ваш название не отражает содержание вопроса. Пожалуйста, прочитайте [Как написать хороший заголовок?] (Http: //meta.stackexchange.com/questions/10647/how-do-i-write-a-good-title) –

ответ

16

Смените GetHashCode на сравниваемое значение. То есть для размера компаратора:

public int GetHashCode(string obj) 
{ 
    return FileInfo(x).Length.GetHashCode(); 
} 

А для других:

public int GetHashCode(string obj) 
{ 
    return Path.GetFileName(obj).GetHashCode(); 
} 

В соответствии с этим ответом - What's the role of GetHashCode in the IEqualityComparer<T> in .NET?, хэш-код вычисляется первым. Equals вызывается в случае столкновения.

Очевидно, было бы разумно работать на FileInfo s, а не на строках.

Так может быть:

FileList.Select(x => new FileInfo(x)) 
     .Distinct(new CustomTextComparer()) 
     .Distinct(new CustomSizeComparer()); 

Конечно, вы должны изменить компараторов работать на правильный тип.

+2

+1: Если экземпляры равны, их хэш-коды также должны быть равны, но если хэш-коды равны, это не обязательно означает, что экземпляры равны. –

4

Хэш-код используется до того, как 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 соответственно, что будет значительным поражением в производительности, тем более, что вы имеете дело с файловой системой, которая точно не известна своей скоростью ответ.

7

Ваш 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(); 
+0

+1 для более простого способа. –

Смежные вопросы