As slepic in his comment, I also wonder why you use andan enumerator in the first and foreach
in the second place?
You can eliminate 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);
Do you really need num
? Couldn't you just return set.Count
?
By using ToHashSet<T>()
directly as show below I only find a minor loss (if any) in performance compared to your versions:
public static class ExtensionsReview
{
public static int DistinctCount<TSource>(this IEnumerable<TSource> source) => DistinctCount(source, (IEqualityComparer<TSource>)null);
public static int DistinctCount<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
{
if (source is null)
{
throw new ArgumentNullException(nameof(source));
}
return source.ToHashSet(comparer).Count;
}
public static int DistinctCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) => DistinctCount(source, predicate, null);
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 source.Where(predicate).ToHashSet(comparer).Count;
}
}
According to your tests, I think you should test reference types (classes) with override of Equals()
/GetHashCode()
(and implementation of IEquatable<T>
) with and without a custom comparer.