When I want to constraint the type T to be comparable, should I use:
where T : IComparable
or
where T : IComparable<T>
I can't get my head around if #2 makes sense. Anyone can explain what the difference would be?
You may want both constraints, as in:
where T : IComparable, IComparable<T>
This would make your type compatible with more users of the IComparable interfaces. The generic version of IComparable, IComparable<T> will help to avoid boxing when T is a value type and allows for more strongly typed implementations of the interface methods. Supporting both ensures that no matter which interface some other object asks for, your object can comply and therefore inter-operate nicely.
For example, Array.Sort and ArrayList.Sort use IComparable, not IComparable<T>.
The main difference between IComparable and IComparable<> is that the first is pre-generics so allows you to call the compare method with any object, whereas the second enforces that it shares the same type:
IComparable - CompareTo(object other);
IComparable<T> - CompareTo(T other);
I would go with the second option provided that you don't intend to use any old .net 1.0 libraries where the types may not implement the modern, generic solution. You'll gain a performance boost since you'll avoid boxing and the comparisons won't need to check the types match and you'll also get the warm feeling that comes from doing things in the most cutting edge way...
To address Jeff's very good and pertinent point I would argue that it is good practice to place as few constraints on a generic as is required to perform the task. Since you are in complete control of the code inside the generic you know whether you are using any methods that require a basic IComparable type. So, taking his comment into consideration I personally would follow these rules:
If you are not expecting the generic to use any types that only implement IComparable (i.e. legacy 1.0 code) and you are not calling any methods from inside the generic that rely on an IComparable parameter then use the IComparable<> constraint only.
If you are using types that only implement IComparable then use that constraint only
If you are using methods that require an IComparable parameter, but not using types that only implement IComparable then using both constraints as in Jeff's answer will boost performance when you use methods that accept the generic type.
To expand on the third rule - let's assume that the class you are writing is as follows:
public class StrangeExample<T> where ... //to be decided
{
public void SortArray(T[] input)
{
Array.Sort(input);
}
public bool AreEqual(T a, T b)
{
return a.CompareTo(b) == 0;
}
}
And we need to decide what constraints to place on it. The SortArray method calls Array.Sort which requires the array that is passed in to contains objects that implement IComparable. Therefore we must have an IComparable constraint:
public class StrangeExample<T> where T : IComparable
Now the class will compile and work correctly as an array of T is valid for Array.Sort and there is a valid .CompareTo method defined in the interface. However, if you are sure that you will not want to use your class with a type that does not also implement the IComparable<> interface you can extend your constraint to:
public class StrangeExample<T> where T : IComparable, IComparable<T>
This means that when AreEqual is called it will use the faster, generic CompareTo method and you will see a performance benefit at the expense of not being able to use it with old, .NET 1.0 types.
On the other hand if you didn't have the AreEqual method then there is no advantage to the IComparable<> constraint so you may as well drop it - you are only using IComparable implementations anyway.
Those are two different interfaces. Before .NET 2.0 there were no generics, so there was just IComparable. With .NET 2.0 came generics and it became possible to make IComparable<T>. They do exactly the same. Basically IComparable is obsolete, although most libraries out there recognize both.
To make your code really compatible, implement both, but make one call the other, so you don't have to write the same code twice.
The IComparable<T> allows the comparator to be strongly typed.
You can have
public int CompareTo(MyType other)
{
// logic
}
as oppose to
public int CompareTo(object other)
{
if (other is MyType)
// logic
}
Take for instance the next example witch implements both interfaces:
public class MyType : IComparable<MyType>, IComparable
{
public MyType(string name, int id)
{ Name = name; Id = id; }
public string Name { get; set; }
public int Id { get; set; }
public int CompareTo(MyType other)
{
if (null == other)
throw new ArgumentNullException("other");
return (Id - other.Id > 0 ? 1 : 0);
}
public int CompareTo(object other)
{
if (null == other)
throw new ArgumentNullException("other");
if (other is MyType)
return (Id - (other as MyType).Id > 0 ? 1 : 0);
else
throw new InvalidOperationException("Bad type");
}
}
MyType t1 = new MyType("a", 1);
MyType t2 = new MyType("b", 2);
object someObj = new object();
// calls the strongly typed method: CompareTo(MyType other)
t1.CompareTo(t2);
// calls the *weakly* typed method: CompareTo(object other)
t1.CompareTo(someObj);
If MyType was only implemented with IComparable<MyType>, the second compareTo(someObj) is a compile time error. This is one advantage of strongly typed generics.
On the other hand, there are methods in the framework that require the non generic IComparable like Array.Sort. In these cases, you should consider implementing both interfaces like in this example.
I would use the second constraint as that will allow you to reference strongly-typed members of the interface. If you go with your first option then you will have to cast to use the interface type.
Related
I know that there is a similar question here, but I would like to see an example, which clearly shows, what you can not do with interface and can with Type Class
For comparison I'll give you an example code:
class Eq a where
(==) :: a -> a -> Bool
instance Eq Integer where
x == y = x `integerEq` y
C# code:
interface Eq<T> { bool Equal(T elem); }
public class Integer : Eq<int>
{
public bool Equal(int elem)
{
return _elem == elem;
}
}
Correct my example, if not correctly understood
Typeclasses are resolved based on a type, while interface dispatch happens against an explicit receiver object. Type class arguments are implicitly provided to a function while objects in C# are provided explicitly. As an example, you could write the following Haskell function which uses the Read class:
readLine :: Read a => IO a
readLine = fmap read getLine
which you can then use as:
readLine :: IO Int
readLine :: IO Bool
and have the appropriate read instance provided by the compiler.
You could try to emulate the Read class in C# with an interface e.g.
public interface Read<T>
{
T Read(string s);
}
but then the implementation of ReadLine would need a parameter for the Read<T> 'instance' you want:
public static T ReadLine<T>(Read<T> r)
{
return r.Read(Console.ReadLine());
}
The Eq typeclass requires both arguments have the same type, whereas your Eq interface does not since the first argument is implicitly the type of the receiver. You could for example have:
public class String : Eq<int>
{
public bool Equal(int e) { return false; }
}
which you cannot represent using Eq. Interfaces hide the type of the receiver and hence the type of one of the arguments, which can cause problems. Imagine you have a typeclass and interface for an immutable heap datastructure:
class Heap h where
merge :: Ord a => h a -> h a -> h a
public interface Heap<T>
{
Heap<T> Merge(Heap<T> other);
}
Merging two binary heaps can be done in O(n) while merging two binomial heaps is possible in O(n log n) and for fibonacci heaps it's O(1). Implementors of the Heap interface do not know the real type of the other heap so is forced to either use a sub-optimal algorithm or use dynamic type checks to discover it. In contrast, types implementing the Heap typeclass do know the representation.
A C# interface defines a set of methods that must be implemented. A Haskell type class defines a set of methods that must be implemented (and possibly a set of default implementations for some of the methods). So there's a lot of similarities there.
(I guess an important difference is that in C#, an interface is a type, whereas Haskell regards types and type classes as strictly separate things.)
The key difference is that in C#, when you define a type (i.e., write a class), you define exactly what interfaces it implements, and this is frozen for all time. In Haskell, you can add new interfaces to an existing type at any time.
For example, if I write a new SerializeToXml interface in C#, I cannot then make double or String implement that interface. But in Haskell, I can define my new SerializeToXml type class, and then make all the standard, built-in types implement that interface (Bool, Double, Int...)
The other thing is how polymorphism works in Haskell. In an OO language, you dispatch on the type of the method the object is being invoked on. In Haskell, the type that the method is implemented for can appear anywhere in the type signature. Most particularly, read dispatches on the return type you want — something you usually can't do at all in OO languages, not even with function overloading.
Also, in C# it's kind of hard to say "these two arguments must have the same type". Then again, OO is predicated on the Liskov substitution principal; two classes that both descend from Customer should be interchangeable, so why would you want to constrain two Customer objects to both be the same type of customer?
Come to think of it, OO languages do method lookup at run-time, whereas Haskell does method lookup at compile-time. This isn't immediately obvious, but Haskell polymorphism actually works more like C++ templates than usual OO polymorphism. (But that's not especially to do with type classes, it's just how Haskell does polymorphism as such.)
Others have already provided excellent answers.
I only want to add a practical example about their differences. Suppose we want to model a "vector space" typeclass/interface, which contains the common operations of 2D, 3D, etc. vectors.
In Haskell:
class Vector a where
scale :: a -> Double -> a
add :: a -> a -> a
data Vec2D = V2 Double Double
instance Vector (Vec2D) where
scale s (V2 x y) = V2 (s*x) (s*y)
add (V2 x1 y1) (V2 x2 y2) = V2 (x1+x2) (y2+y2)
-- the same for Vec3D
In C#, we might try the following wrong approach (I hope I get the syntax right)
interface IVector {
IVector scale(double s);
IVector add(IVector v);
}
class Vec2D : IVector {
double x,y;
// constructor omitted
IVector scale(double s) {
return new Vec2D(s*x, s*y);
}
IVector add(IVector v) {
return new Vec2D(x+v.x, y+v.y);
}
}
We have two issues here.
First, scale returns only an IVector, a supertype of the actual Vec2D. This is bad, because scaling does not preserve the type information.
Second, add is ill-typed! We can't use v.x since v is an arbitrary IVector which might not have the x field.
Indeed, the interface itself is wrong: the add method promises that any vector must be summable with any other vector, so we must be able to sum 2D and 3D vectors, which is nonsense.
The usual solution is to switch to F-bounded quantification AKA CRTP or whatever it's being called these days:
interface IVector<T> {
T scale(double s);
T add(T v);
}
class Vec2D : IVector<Vec2D> {
double x,y;
// constructor omitted
Vec2D scale(double s) {
return new Vec2D(s*x, s*y);
}
Vec2D add(Vec2D v) {
return new Vec2D(x+v.x, y+v.y);
}
}
The first time a programmer meets this, they are usually puzzled by the seemingly "recursive" line Vec2D : IVector<Vec2D>. I surely was :) Then we get used to this and accept it as an idiomatic solution.
Type classes arguably have a nicer solution here.
After a long study of this issue, I came to an easy method of explaining. At least for me it's clear.
Imagine we have method with signature like this
public static T[] Sort(T[] array, IComparator<T> comparator)
{
...
}
And implementation of IComparator:
public class IntegerComparator : IComparator<int> { }
Then we can write code like this:
var sortedIntegers = Sort(integers, new IntegerComparator());
We can improve this code, first we create Dictionary<Type, IComparator> and fill it:
var comparators = new Dictionary<Type, IComparator>()
{
[typeof(int)] = new IntegerComparator(),
[typeof(string)] = new StringComparator()
}
Redesigned IComparator interface so that we could write like above
public interface IComparator {}
public interface IComparator<T> : IComparator {}
And after this let's redesign Sort method signature
public class SortController
{
public T[] Sort(T[] array, [Injectable]IComparator<T> comparator = null)
{
...
}
}
As you understand we are going to inject IComparator<T>, and write code like this:
new SortController().Sort<int>(integers, (IComparator<int>)_somparators[typeof(int)])
As you already guessed this code will not work for other types until we outline the implementation and add in Dictionary<Type, IComparator>
Notice, the exception we will see only on runtime
And now imagine if this work was done for us by the compiler during build and it threw exception if it could not find the comparator with corresponding types.
For this, we could help the compiler and add a new keyword instead of usage attribute. Out Sort method will be look like this:
public static T[] Sort(T[] array, implicit IComparator<T> comparator)
{
...
}
And code of realization concrete Comparator:
public class IntegerComparator : IComparator<int> implicit { }
Note, we use the keyword 'implicit', after this compiler will be able to do
routine work, which we wrote above, and the exception will be thrown during
compile-time
var sortedIntegers = Sort(integers);
// this gives us compile-time error
// because we don't have implementation of IComparator<string>
var sortedStrings = Sort(strings);
And give the name to this style of implementation Type Class
public class IntegerComparator : IComparator<int> implicit { }
I hope that I understood correctly and understandably explained.
PS: The code does not pretend to work.
I am aware of the fact that I always have to override Equals(object) and GetHashCode() when implementing IEquatable<T>.Equals(T).
However, I don't understand, why in some situations the Equals(object) wins over the generic Equals(T).
For example why is the following happening? If I declare IEquatable<T> for an interface and implement a concrete type X for it, the general Equals(object) is called by a Hashset<X> when comparing items of those type against each other. In all other situations where at least one of the sides is cast to the Interface, the correct Equals(T) is called.
Here's a code sample to demonstrate:
public interface IPerson : IEquatable<IPerson> { }
//Simple example implementation of Equals (returns always true)
class Person : IPerson
{
public bool Equals(IPerson other)
{
return true;
}
public override bool Equals(object obj)
{
return true;
}
public override int GetHashCode()
{
return 0;
}
}
private static void doEqualityCompares()
{
var t1 = new Person();
var hst = new HashSet<Person>();
var hsi = new HashSet<IPerson>();
hst.Add(t1);
hsi.Add(t1);
//Direct comparison
t1.Equals(t1); //IEquatable<T>.Equals(T)
hst.Contains(t1); //Equals(object) --> why? both sides inherit of IPerson...
hst.Contains((IPerson)t1); //IEquatable<T>.Equals(T)
hsi.Contains(t1); //IEquatable<T>.Equals(T)
hsi.Contains((IPerson)t1); //IEquatable<T>.Equals(T)
}
HashSet<T> calls EqualityComparer<T>.Default to get the default equality comparer when no comparer is provided.
EqualityComparer<T>.Default determines if T implementsIEquatable<T>. If it does, it uses that, if not, it uses object.Equals and object.GetHashCode.
Your Person object implements IEquatable<IPerson> not IEquatable<Person>.
When you have a HashSet<Person> it ends up checking if Person is an IEquatable<Person>, which its not, so it uses the object methods.
When you have a HashSet<IPerson> it checks if IPerson is an IEquatable<IPerson>, which it is, so it uses those methods.
As for the remaining case, why does the line:
hst.Contains((IPerson)t1);
call the IEquatable Equals method even though its called on the HashSet<Person>. Here you're calling Contains on a HashSet<Person> and passing in an IPerson. HashSet<Person>.Contains requires the parameter to be a Person; an IPerson is not a valid argument. However, a HashSet<Person> is also an IEnumerable<Person>, and since IEnumerable<T> is covariant, that means it can be treated as an IEnumerable<IPerson>, which has a Contains extension method (through LINQ) which accepts an IPerson as a parameter.
IEnumerable.Contains also uses EqualityComparer<T>.Default to get its equality comparer when none is provided. In the case of this method call we're actually calling Contains on an IEnumerable<IPerson>, which means EqualityComparer<IPerson>.Default is checking to see if IPerson is an IEquatable<IPerson>, which it is, so that Equals method is called.
Although IComparable<in T> is contravariant with respect to T, such that any type which implements IComparable<Person> would automatically be considered an implementation of IComparable<IPerson>, the type IEquatable<T> is intended for use with sealed types, especially structures. The requirement that Object.GetHashCode() be consistent with both IEquatable<T>.Equals(T) and Object.Equals(Object) generally implies that the latter two methods should behave identically, which in turn implies that one of them should chain to the other. While there is a large performance difference between passing a struct directly to an IEquatable<T> implementation of the proper type, compared with constructing a instance of the structure's boxed-heap-object type and having an Equals(Object) implementation copy the structure data out of that, no such performance different exists with reference types. If IEquatable<T>.Equals(T) and Equals(Object) are going to be equivalent and T is an inheritable reference type, there's no meaningful difference between:
bool Equals(MyType obj)
{
MyType other = obj as MyType;
if (other==null || other.GetType() != typeof(this))
return false;
... test whether other matches this
}
bool Equals(MyType other)
{
if (other==null || other.GetType() != typeof(this))
return false;
... test whether other matches this
}
The latter could save one typecast, but that's unlikely to make a sufficient performance difference to justify having two methods.
This questions involves 2 different implementations of essentially the same code.
First, using delegate to create a Comparison method that can be used as a parameter when sorting a collection of objects:
class Foo
{
public static Comparison<Foo> BarComparison = delegate(Foo foo1, Foo foo2)
{
return foo1.Bar.CompareTo(foo2.Bar);
};
}
I use the above when I want to have a way of sorting a collection of Foo objects in a different way than my CompareTo function offers. For example:
List<Foo> fooList = new List<Foo>();
fooList.Sort(BarComparison);
Second, using IComparer:
public class BarComparer : IComparer<Foo>
{
public int Compare(Foo foo1, Foo foo2)
{
return foo1.Bar.CompareTo(foo2.Bar);
}
}
I use the above when I want to do a binary search for a Foo object in a collection of Foo objects. For example:
BarComparer comparer = new BarComparer();
List<Foo> fooList = new List<Foo>();
Foo foo = new Foo();
int index = fooList.BinarySearch(foo, comparer);
My questions are:
What are the advantages and disadvantages of each of these implementations?
What are some more ways to take advantage of each of these implementations?
Is there a way to combine these implementations in such a way that I do not need to duplicate the code?
Can I achieve both a binary search and an alternative collection sort using only 1 of these implementations?
There really is no advantage to either option in terms of performance. It's really a matter of convenience and code maintainability. Choose the option you prefer. That being said, the methods in question limit your choices slightly.
You can use the IComparer<T> interface for List<T>.Sort, which would allow you to not duplicate code.
Unfortunately, BinarySearch does not implement an option using a Comparison<T>, so you cannot use a Comparison<T> delegate for that method (at least not directly).
If you really wanted to use Comparison<T> for both, you could make a generic IComparer<T> implementation that took a Comparison<T> delegate in its constructor, and implemented IComparer<T>.
public class ComparisonComparer<T> : IComparer<T>
{
private Comparison<T> method;
public ComparisonComparer(Comparison<T> comparison)
{
this.method = comparison;
}
public int Compare(T arg1, T arg2)
{
return method(arg1, arg2);
}
}
Probably the biggest advantage to accepting a Comparison<T> as opposed to an IComparer<T> is the ability to write anonymous methods. If I have, let's say, a List<MyClass>, where MyClass contains an ID property that should be used for sorting, I can write:
myList.Sort((c1, c2) => c1.ID.CompareTo(c2.ID));
Which is a lot more convenient than having to write an entire IComparer<MyClass> implementation.
I'm not sure that accepting an IComparer<T> really has any major advantages, except for compatibility with legacy code (including .NET Framework classes). The Comparer<T>.Default property is only really useful for primitive types; everything else usually requires extra work to code against.
To avoid code duplication when I need to work with IComparer<T>, one thing I usually do is create a generic comparer, like this:
public class AnonymousComparer<T> : IComparer<T>
{
private Comparison<T> comparison;
public AnonymousComparer(Comparison<T> comparison)
{
if (comparison == null)
throw new ArgumentNullException("comparison");
this.comparison = comparison;
}
public int Compare(T x, T y)
{
return comparison(x, y);
}
}
This allows writing code such as:
myList.BinarySearch(item,
new AnonymousComparer<MyClass>(x.ID.CompareTo(y.ID)));
It's not exactly pretty, but it saves some time.
Another useful class I have is this one:
public class PropertyComparer<T, TProp> : IComparer<T>
where TProp : IComparable
{
private Func<T, TProp> func;
public PropertyComparer(Func<T, TProp> func)
{
if (func == null)
throw new ArgumentNullException("func");
this.func = func;
}
public int Compare(T x, T y)
{
TProp px = func(x);
TProp py = func(y);
return px.CompareTo(py);
}
}
Which you can write code designed for IComparer<T> as:
myList.BinarySearch(item, new PropertyComparer<MyClass, int>(c => c.ID));
The delegate technique is very short (lambda expressions might be even shorter), so if shorter code is your goal, then this is an advantage.
However, implementing the IComparer (and its generic equivalent) makes your code more testable: you can add some unit testing to your comparing class/method.
Furthermore, you can reuse your comparer implementation when composing two or more comparers and combining them as a new comparer. Code reuse with anonymous delegates is harder to achieve.
So, to sum it up:
Anonymous Delegates: shorter (and perhaps cleaner) code
Explicit Implementation: testability and code reuse.
They really address different needs:
IComparable is useful for objects that are ordered. Real numbers should be comparable, but complex numbers cannot - it is ill-defined.
IComparer allows to define re-usable, well-encapsulated comparers. This is especially useful if the comparison needs to know some additional information. For example, you might want to compare dates and times from different time zones. That can be complicated, and a separate comparer should be used for this purpose.
A comparison method is made for simple comparison operations that are not complicated enough for reusability to be of any concern, e.g. sorting a list of customers by their first name. This is simple operation, hence does not need additional data. Likewise, this is not inherent to the object, because the objects are not naturally ordered in any way.
Lastly, there is IEquatable, which might be important if your Equals method can only decide if two objects are equal or not, but if there is no notion of 'larger' and 'smaller', e.g. complex numbers, or vectors in space.
In your case, advantage of having an IComparer<T> over Comparision<T> delegate, is that you can also use it for the Sort method, so you don't need a Comparison delegate version at all.
Another useful thing you can do is implementing a delegated IComparer<T> implementation like this:
public class DelegatedComparer<T> : IComparer<T>
{
Func<T,T,int> _comparision;
public DelegatedComparer(Func<T,T,int> comparision)
{
_comparision = comparision;
}
public int Compare(T a,T b) { return _comparision(a,b); }
}
list.Sort(new DelegatedComparer<Foo>((foo1,foo2)=>foo1.Bar.CompareTo(foo2.Bar));
and a more advanced version:
public class PropertyDelegatorComparer<TSource,TProjected> : DelegatedComparer<TSource>
{
PropertyDelegatorComparer(Func<TSource,TProjected> projection)
: base((a,b)=>projection(a).CompareTo(projection(b)))
}
So I am utilizing CollectionBase as an inherited class for custom collections. I am utilizing CollectionBase through an abstract class so that I don't repeated knowledge (following the DRY principle). The abstract class is defined as a generic class also. Here is how I am implementing my class:
public abstract class GenericCollectionBase<T,C> : CollectionBase
{
//Indexders, virtual methods for Add, Contains, IndexOf, etc
}
I utilize this so I don't have to implement these base methods in 10+ classes.
My question is am I taking this too far when I override the Equals method like this:
public override bool Equals(object obj)
{
if (obj is C)
{
GenericCollectionBase<T, C> collB =
obj as GenericCollectionBase<T, C>;
if (this.Count == collB.Count)
{
for (int i = 0; i < this.Count; ++i)
{
if (!this[i].Equals(collB[i]))
return false;
}
return true;
}
}
return false;
}
Am I trying to accomplish too much with my abstract, or doing this the correct way?
EDIT: This is written for .Net 2.0 and do not have access to 3.5 to utilize things like LINQ
I don't believe you are trying to accomplish too much. If an abstract class was meant to not have any implementation at all, or other methods which define functionality, then they would be interfaces.
The only thing I would change is to use EqualityComparer<T> instead of equals for the comparison of this[i] and collB[i].
Well, first, this is weird :
if (obj is C)
{
GenericCollectionBase<T, C> collB = obj as GenericCollectionBase<T, C>;
I'll assume you meant that :
GenericCollectionBase<T, C> collB = obj as GenericCollectionBase<T, C>;
if (collB != null)
{
...
I think you're over-thinking this, except if you really, really need two different collections with the same content to be considered as equal. I'd put this logic in another method to be called explicitly or in an equality comparer.
Making an extension method against IDictionary would be far more useful. There's also methods like Intersect from LINQ that may be useful.
I don't know if you're trying to accomplish too much, but I think you're trying to accomplish the wrong thing. There are cases where you might want that type of equality for collections, but it should be opt-in and obvious from the name of the type. I've created a ListValue<> with the type of equality you're using, but then it's always been immutable as well.
Also, if you're going to do this type of equality check, an initial test using object.ReferenceEquals can save you from having to iterate over a large collection when your comparing an object to itself.
I'm looking for real world best practices, how other people might have implemented solutions with complex domains.
Any time you consider using an IEqualityComparer<T>, pause to think if the class could be made to implement IEquatable<T> instead. If a Product should always be compared by ID, just define it to be equated as such so you can use the default comparer.
That said, there are still a few of reasons you might want a custom comparer:
If there are multiple ways instances of a class could be considered equal. The best example of this is a string, for which the framework provides six different comparers in StringComparer.
If the class is defined in such a way that you can't define it as IEquatable<T>. This would include classes defined by others and classes generated by the compiler (specifically anonymous types, which use a property-wise comparison by default).
If you do decide you need a comparer, you can certainly use a generalized comparer (see DMenT's answer), but if you need to reuse that logic you should encapsulate it in a dedicated class. You could even declare it by inheriting from the generic base:
class ProductByIdComparer : GenericEqualityComparer<ShopByProduct>
{
public ProductByIdComparer()
: base((x, y) => x.ProductId == y.ProductId, z => z.ProductId)
{ }
}
As far as use, you should take advantage of comparers when possible. For example, rather than calling ToLower() on every string used as a dictionary key (logic for which will be strewn across your app), you should declare the dictionary to use a case-insensitive StringComparer. The same goes for the LINQ operators that accept a comparer. But again, always consider if the equatable behavior that should be intrinsic to the class rather than defined externally.
I did the following, I'm not sure if it is real-world best practice, but it worked fine for me. :)
public class GenericEqualityComparer<T> : IEqualityComparer<T>
{
private Func<T, T, Boolean> _comparer;
private Func<T, int> _hashCodeEvaluator;
public GenericEqualityComparer(Func<T, T, Boolean> comparer)
{
_comparer = comparer;
}
public GenericEqualityComparer(Func<T, T, Boolean> comparer, Func<T, int> hashCodeEvaluator)
{
_comparer = comparer;
_hashCodeEvaluator = hashCodeEvaluator;
}
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
return _comparer(x, y);
}
public int GetHashCode(T obj)
{
if(obj == null) {
throw new ArgumentNullException("obj");
}
if(_hashCodeEvaluator == null) {
return 0;
}
return _hashCodeEvaluator(obj);
}
#endregion
}
Then you can use it in your collections.
var comparer = new GenericEqualityComparer<ShopByProduct>((x, y) => x.ProductId == y.ProductId);
var current = SelectAll().Where(p => p.ShopByGroup == group).ToList();
var toDelete = current.Except(products, comparer);
var toAdd = products.Except(current, comparer);
If you need to support custom GetHashCode() functionality, use the alternative constructor to provide a lambda to do the alternative calculation:
var comparer = new GenericEqualityComparer<ShopByProduct>(
(x, y) => { return x.ProductId == y.ProductId; },
(x) => { return x.Product.GetHashCode()}
);
I hope this helps. =)
See this post for (better) alternatives: Wrap a delegate in an IEqualityComparer
Scroll down to the part on KeyEqualityComparer and especially the part on the importance of GetHashCode. There is a whole discussion on why obj.GetHashCode(); (as suggested by DMenT's post) is wrong and should just return 0 instead.
This is what MSDN has to say about IEqualityComparer (non-generic):
This interface allows the implementation of customized equality comparison for collections. That is, you can create your own definition of equality, and specify that this definition be used with a collection type that accepts the IEqualityComparer interface. In the .NET Framework, constructors of the Hashtable, NameValueCollection, and OrderedDictionary collection types accept this interface.
This interface supports only equality comparisons. Customization of comparisons for sorting and ordering is provided by the IComparer interface.
It looks like the generic version of this interface performs the same function but is used for Dictionary<(Of <(TKey, TValue>)>) collections.
As far as best practices around using this interface for your own purposes. I would say that the best practice would be to use it when you are deriving or implementing a class that has similar functionality to the above mentioned .NET framework collections and where you want to add the same capability to your own collections. This will ensure that you are consistent with how the .NET framework uses the interface.
In other words support the use of this interface if you are developing a custom collection and you want to allow your consumers to control equality which is used in a number of LINQ and collection related methods (eg. Sort).
I would say that the best use would be when you need to plug in different equality rules for a certain algorithm. Much in the same way that a sorting algorithm might accept an IComparer<T>, a finding algorithm might accept an IEqualityComparer<T>
The list uses this interface alot, so you can say a.Substract(b) or other of these nice functions.
Just remember: If you're objects don't return the same Hashcode, the Equals is not called.