As slepic in his comment, I also wonder why you use and enumerator in the first and `foreach` in the second place? ---------- You can minimize the `null` checks in the versions that call other overrides: > public static int DistinctCount<TSource>(this IEnumerable<TSource> source) => > source?.DistinctCount((IEqualityComparer<TSource>)null) ?? throw new ArgumentNullException(nameof(source)); can be reduced to: public static int DistinctCount<TSource>(this IEnumerable<TSource> source) => DistinctCount(source, (IEqualityComparer<TSource>)null); And the other to: public static int DistinctCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) => DistinctCount(source, predicate, null); ---------- You could create a common counter method as: private static int Count<TSource>(IEnumerable<TSource> source, Func<TSource, ISet<TSource>, bool> predicate, IEqualityComparer<TSource> comparer) { ISet<TSource> set = new HashSet<TSource>(comparer); int num = 0; using (IEnumerator<TSource> enumerator = source.GetEnumerator()) { while (enumerator.MoveNext()) { if (predicate(enumerator.Current, set)) { checked { ++num; } } } } return num; } Called in the first as: public static int DistinctCount<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer) { if (source is null) { throw new ArgumentNullException(nameof(source)); } return Count(source, (o, set) => set.Add(o), comparer); } and in the latter as: public static int DistinctCount<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate, IEqualityComparer<TSource> comparer) { if (source is null) { throw new ArgumentNullException(nameof(source)); } if (predicate is null) { throw new ArgumentNullException(nameof(predicate)); } return Count(source, (o, set) => predicate(o) && set.Add(o), comparer); }