I have some code that looks like the following. First I have some domain classes and some special comparators for them.
public class Fruit {
public int Calories { get; set; }
public string Name { get; set; }
}
public class FruitEqualityComparer : IEqualityComparer<Fruit> {
// ...
}
// A basket is just a group of Fruits.
public class BasketEqualityComparer : IEqualityComparer<IEnumerable<Fruit>> {
// ...
}
Next, I have a helper class called ConstraintChecker
. It has a simple BaseEquals
method that makes sure some simple base cases are considered:
public static class ConstraintChecker {
public static bool BaseEquals(T lhs, T rhs) {
bool sameObject = l == r;
bool leftNull = l == null;
bool rightNull = r == null;
return sameObject && !leftNull && !rightNull;
}
There's also a SemanticEquals
method which is just a BaseEquals
check and a comparator function that you specify.
public static bool SemanticEquals<T>(
T lhs, T rhs, Func<T, T, bool> f) {
return BaseEquals(lhs, rhs) && f(lhs, rhs);
}
And finally there's a Sema开发者_运维技巧nticSequenceEquals
method which accepts two IEnumerable<T>
instances to compare, and an IEqualityComparer instance that will get called on each pair of elements in the list via Enumerable.SequenceEquals
.
public static bool SemanticSequenceEquals<T, U, V>(U lhs,
U rhs,
V comparator)
where U : IEnumerable<T>
where V : IEqualityComparer<T> {
return SemanticEquals(lhs, rhs, (l, r) => lhs.SequenceEqual(rhs, comparator));
}
} // end of ConstraintChecker
The point of SemanticSequenceEquals
is that you don't have to define two comparators whenever you want to compare both IEnumerable<T>
and T
instances; now you can just specify an IEqualityComparer<T>
and it will also handle lists when you invoke SemanticSequenceEquals
. So I could get rid of the BasketEqualityComparer class, which would be nice.
But there's a problem. The C# compiler can't figure out the types involved when you invoke SemanticSequenceEquals:
// Error! Compiler can't infer the type parameters.
return ConstraintChecker.SemanticSequenceEquals(lhs, rhs,
new FruitEqualityComparer());
If I specify them explicitly, it works:
return ConstraintChecker.SemanticSequenceEquals<Fruit, IEnumerable<Fruit>,
IEqualityComparer<Fruit>> (lhs, rhs, new FruitEqualityComparer());
Obviously, that's a huge hassle, and it's not very DRY. What can I change here so that I don't have to write the type parameters explicitly?
Try just specifying T without the U and V like this.
public static bool SemanticSequenceEquals<T>(
IEnumerable<T> lhs,
IEnumerable<T> rhs,
IEqualityComparer<T> comparator)
{
return SemanticEquals(lhs, rhs, (l, r) => lhs.SequenceEqual(rhs, comparator));
}
You could explicitly type your arguments to SemanticSequenceEquals. This seems to compile fine for me:
public static bool SemanticSequenceEquals<T>(IEnumerable<T> lhs, IEnumerable<T> rhs, IEqualityComparer<T> comparator)
{
return SemanticEquals(lhs, rhs, (l, r) => lhs.SequenceEqual(rhs, comparator));
}
List<Fruit> a, b;
return ConstraintChecker.SemanticSequenceEquals(a, b, new FruitEqualityComparer());
精彩评论