动机
我希望实现 IComparer<>
以类似于下面的演示代码的方式。在哪里Foo
是我需要比较的对象类型。它没有实现 IComparable
,但我提供了一个 IComparer
每个字段的类,以便用户可以选择等同于基于一个字段值的实例。
enum Day {Sat, Sun, Mon, Tue, Wed, Thu, Fri};
class Foo {
public int Bar;
public string Name;
public Day Day;
}
比较类是:
// Compares all fields in Foo
public class FooComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
if (ReferenceEquals(x, y)) return true;
return x.Bar == y.Bar && x.Name == y.Name && return x.Day == y.Day;
}
public int GetHashCode(Foo obj)
{
unchecked
{
var hashCode = obj.Bar;
hashCode = (hashCode * 397) ^ (obj.Name != null ? obj.Name.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (int) obj.Day; 0);
return hashCode;
}
}
}
// Compares only in Foo.Bar
public class FooBarComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
if (ReferenceEquals(x, y)) return true;
return x.Bar == y.Bar;
}
public int GetHashCode(Foo obj)
{
unchecked
{
var hashCode = obj.Bar;
hashCode = (hashCode * 397) ^ (obj.Name != null ? obj.Name.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (int) obj.Day; 0);
return hashCode;
}
}
}
// Compares only in Foo.Name
public class FooNameComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
if (ReferenceEquals(x, y)) return true;
return x.Name == y.Name;
}
public int GetHashCode(Foo obj)
{
unchecked
{
var hashCode = obj.Bar;
hashCode = (hashCode * 397) ^ (obj.Name != null ? obj.Name.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (int) obj.Day; 0);
return hashCode;
}
}
}
// Compares only in Foo.Day
public class FooDayComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
if (ReferenceEquals(x, y)) return true;
return x.Day == y.Day;
}
public int GetHashCode(Foo obj)
{
unchecked
{
var hashCode = obj.Bar;
hashCode = (hashCode * 397) ^ (obj.Name != null ? obj.Name.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (int) obj.Day; 0);
return hashCode;
}
}
}
问题
我想让用户能够组合多个 Comparer
类型来评估类型的两个实例 Foo
.我不确定该怎么做。
想法
我想出的是这样的,我 AND
列表中所有比较器的比较结果:
bool CompareFoo(Foo a, Foo b, params IComparer[] comparers)
{
bool isEqual = true;
// Or the list and return;
foreach (var comparer in comparers)
{
isEqual = isEqual && comparer.Equals(x,y);
}
return isEqual;
}
注意事项
- 我的目标 .NET 版本是
4.5
. - 我可能会卡在
C# 5.0
上. - 此外,可能会卡在 `MSBuild 12.0
- 这是我第一次使用
IComparer
.
最佳答案
您可以组合多个 IEqualityComparer<Foo>
通过定义一个将其他比较器作为构造函数参数的附加比较器来对象:
public class CompositeFooComparer : IEqualityComparer<Foo>
{
private IEqualityComparer<Foo>[] comparers;
public CompositeFooComparer(params IEqualityComparer<Foo>[] comparers)
{
this.comparers = comparers;
}
public bool Equals(Foo x, Foo y)
{
foreach (var comparer in comparers)
{
if (!comparer.Equals(x, y))
{
return false;
}
}
return true;
}
public int GetHashCode(Foo obj)
{
var hash = 0;
foreach (var comparer in comparers)
{
hash = hash * 17 + (comparer.GetHashCode(obj));
}
return hash;
}
}
然后你可以像这样创建和使用它:
var fooA = new Foo
{
Bar = 5,
Day = Day.Fri,
Name = "a"
};
var fooB = new Foo
{
Bar = 5,
Day = Day.Fri,
Name = "b"
};
var barComparer = new FooBarComparer();
var dayComparer = new FooDayComparer();
var compositeComparer = new CompositeFooComparer(barComparer, dayComparer);
Console.WriteLine(compositeComparer.Equals(fooA, fooB)); // displays "true"
另一个想法是有一个比较器确实知道将比较哪些字段,而不是基于 bool 参数。
public class ConfigurableFooComparer : IEqualityComparer<Foo>
{
private readonly bool compareBar;
private readonly bool compareName;
private readonly bool compareDay;
public ConfigurableFooComparer(bool compareBar, bool compareName, bool compareDay)
{
this.compareBar = compareBar;
this.compareName = compareName;
this.compareDay = compareDay;
}
public bool Equals(Foo x, Foo y)
{
if (ReferenceEquals(x, y))
{
return true;
}
if (x == null || y == null)
{
return false;
}
if (compareBar && x.Bar != y.Bar)
{
return false;
}
if (compareName && x.Name != y.Name)
{
return false;
}
if (compareDay && x.Day != y.Day)
{
return false;
}
return true;
}
public int GetHashCode(Foo obj)
{
unchecked
{
var hash = 0;
if (compareBar)
{
hash = hash * 17 + obj.Bar.GetHashCode();
}
if (compareName)
{
hash = hash * 17 + (obj.Name == null ? 0 : obj.Name.GetHashCode());
}
if (compareDay)
{
hash = hash * 17 + obj.Day.GetHashCode();
}
return hash;
}
}
然后像这样使用它:
var barAndDayComparer = new ConfigurableFooComparer(compareBar: true, compareName: false, compareDay: true);
Console.WriteLine(barAndDayComparer.Equals(fooA, fooB));
关于C#:如何在检查相等性时评估一个对象的多个计算机类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52341265/