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, pause to think if the class could be made to implement IEquatable 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:
IEquatable. 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
{
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.