Wrap a delegate in an IEqualityComparer
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
LINQ methods like Distinct(), GroupBy(), and Except() accept an IEqualityComparer<T> parameter, but creating a full class for each comparison is verbose. A delegate wrapper lets you pass a lambda instead. You create a generic class that implements IEqualityComparer<T> by wrapping a Func<T, T, bool> for equality and optionally a Func<T, int> for hash codes. This pattern eliminates boilerplate when you need one-off custom comparisons.
The Problem
The Solution: Delegate Wrapper
Usage
Simplified Version: Key-Based Comparer
Most custom comparisons compare a single property. A key-based comparer is even cleaner:
Extension Method for Fluent API
Note: .NET 6+ includes Enumerable.DistinctBy natively, making this extension unnecessary for newer frameworks.
Using with Dictionaries and HashSets
GroupBy and Except with Custom Comparer
Common Pitfalls
- Forgetting
GetHashCode:IEqualityComparer<T>requires bothEqualsandGetHashCode. IfGetHashCodereturns different values for "equal" objects, hash-based collections (HashSet,Dictionary,Distinct) will treat them as different even thoughEqualsreturns true. - Using a constant hash code: Returning a constant like
0fromGetHashCodeis technically correct but degrades hash-based collections to O(n) performance per lookup (every object lands in the same bucket). Always derive the hash from the same fields used inEquals. - Null reference in delegate: If the delegate does not handle null arguments, passing a collection with null elements causes
NullReferenceException. The wrapper should check for null before invoking the delegate. - Mutable keys: If the key used for equality (e.g.,
Name) changes after the object is added to aHashSetorDictionary, the object becomes unreachable. The hash code at insertion time no longer matches. Only use immutable properties as keys. - Not using .NET 6+
DistinctBy: Starting with .NET 6,Enumerable.DistinctBy,UnionBy,ExceptBy, andIntersectByare built in. If targeting .NET 6+, use these instead of building custom comparers.
Summary
- Wrap
Func<T, T, bool>andFunc<T, int>in anIEqualityComparer<T>class to pass lambdas to LINQ methods - A key-selector comparer (
Func<T, TKey>) covers most use cases with less code - Always implement both
EqualsandGetHashCodeconsistently — hash-based collections depend on both - .NET 6+ provides
DistinctBy,ExceptBy, etc., eliminating the need for custom comparers in many cases - Use
EqualityComparer<TKey>.Defaultinside the wrapper to leverage built-in equality for the key type
Related reading
- Wrapping ManualResetEvent as awaitable task
- Wrapping StopWatch timing with a delegate or lambda?
- Write to Windows Application Event Log without event source registration
- WriteFile blocks writing from C client to C server via named pipe
- Writing to a TextBox from another thread?
- X509Certificate Constructor Exception
- Xml serialization - Hide null values
- XML serialization of interface property

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.