----------


As slepic in his comment, I also wonder why you use and 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.