IEqualityComparer未按预期工作

时间:2014-01-28 10:05:55

标签: c# .net linq iequalitycomparer

我的计算机上存有List个文件路径。我的目的是首先筛选出具有相同名称的文件,然后筛选出具有相同大小的文件 为此,我创建了两个实现IEqualityComparer<string>的类,并实现了EqualsGetHashCode方法。

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

但代码不起作用。

它不抛出任何异常,也没有任何编译器错误,但问题是代码不起作用(不排除重复文件)。

那么,我该如何纠正这个问题呢?我能做些什么来使代码正常工作。

3 个答案:

答案 0 :(得分: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());

当然,您必须更改比较器才能使用正确的类型。

答案 1 :(得分: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();

答案 2 :(得分:5)

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

然而,正如Piotr指出的那样,这不是一个很好的方法来实现你的目标,因为你将要做Path.GetFileName和{{批次 1}},这将是一个重大的性能损失,特别是因为你正在处理文件系统,而这个文件系统的响应速度并不完全清楚。