2014-01-28 10 views
10

Ich habe eine List der Pfade von Dateien auf meinem Computer gespeichert. Mein Ziel ist es, zuerst die gleichnamigen Dateien herauszufiltern und dann diejenigen herauszufiltern, die die gleiche Größe haben.
Um dies zu tun, habe ich zwei Klassen implementiert IEqualityComparer<string> und implementiert Equals und GetHashCode Methoden.IEqualityComparer funktioniert nicht wie vorgesehen

var query = FilesList.Distinct(new CustomTextComparer()) 
        .Distinct(new CustomSizeComparer()); 

Der Code für beide der Klassen ist unten angegeben: -

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

Aber der Code funktioniert nicht.

Es gibt keine Ausnahmen noch gibt es einen Compilerfehler, aber das Problem ist, dass der Code nicht funktioniert (schließt doppelte Dateien nicht aus).

Also, wie kann ich dieses Problem beheben? Kann ich etwas tun, damit der Code korrekt funktioniert?

+0

Ihre Der Titel spiegelt nicht den Inhalt der Frage wider. Bitte lesen Sie [Wie schreibe ich einen guten Titel?] (Http: //meta.stackexchange.com/questions/10647/how-do-ich-schreibe-einen-guten titel) –

Antwort

16

Ändern Sie Ihre GetHashCode, um mit dem verglichenen Wert zu arbeiten. I.e. für Ihre Größe Vergleich:

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

Und zum anderen:

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

Nach dieser Antwort - What's the role of GetHashCode in the IEqualityComparer<T> in .NET? wird der Hash-Code zuerst ausgewertet. Equals wird im Falle einer Kollision aufgerufen.

Offensichtlich wäre es sinnvoll, an FileInfo s zu arbeiten, nicht an Strings.

Also vielleicht:

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

Natürlich, dann müssen Sie Ihre comparers ändern auf den richtigen Typ zu arbeiten.

+2

+1: Immer wenn Instanzen gleich sind ihre Hash-Codes müssen auch gleich sein, aber wenn Hash-Codes gleich sind, bedeutet es nicht unbedingt, dass Instanzen sind gleich. –

4

Der Hash-Code wird verwendet, bevor Equals überhaupt aufgerufen wird. Da Ihr Code verschiedene Hash-Codes für gleichwertige Elemente liefert, erhalten Sie nicht das gewünschte Ergebnis. Stattdessen müssen Sie der Hash-Code sicher zurück gleich ist, wenn die Elemente gleich sind, so zum Beispiel:

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

Doch wie Piotr darauf hingewiesen, ist dies nicht gerade ein guter Weg, um Ihr Ziel zu gehen , da Sie eine Los von Path.GetFileName bzw. new FileInfo tun werden, die ein bedeutender Leistungserfolg sein wird, besonders, da Sie mit dem Dateisystem beschäftigen, das für seine Geschwindigkeit nicht gerade bekannt ist Antwort.

7

Ihre GetHashCode muss denselben Wert für alle Objekte zurück, die gleichwertig sind:

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

Aber das ist eine viel einfachere Art und Weise für das gesamte Problem ohne die zusätzlichen Klassen:

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 für den leichteren Weg. –