dotnet/reactive · error · ArgumentNullException

Thrown when comparer is null (ArgumentNullException, param…

Error message

Thrown when comparer is null (ArgumentNullException, param name: comparer)

What it means

The comparer-based MaxBy overload requires a non-null IComparer<TKey> to compare extracted keys. Passing null throws ArgumentNullException with paramName 'comparer' immediately, before enumeration. The overload is obsolete in favor of MaxByWithTies.

Solutions

  1. Pass Comparer<TKey>.Default when no custom comparison is required.
  2. Default the comparer before the call: comparer ??= Comparer<TKey>.Default;
  3. Fix the comparer source (register it in DI/registry) and consider migrating to MaxByWithTies.

Example fix

// before
var top = items.MaxBy(x => x.Key, resolver.GetComparer<string>()); // null if unregistered
// after
var cmp = resolver.GetComparer<string>() ?? Comparer<string>.Default;
var top = items.MaxByWithTies(x => x.Key, cmp);
Defensive patterns

Strategy: validation

Validate before calling

if (comparer is null) comparer = Comparer<TKey>.Default;
var top = source.MaxBy(keySelector, comparer); // prefer MaxByWithTies

Type guard

static IComparer<TKey> NonNullComparer<TKey>(IComparer<TKey>? c) => c ?? Comparer<TKey>.Default;

Try / catch

try
{
    var top = source.MaxBy(keySelector, comparer);
}
catch (ArgumentNullException ex) when (ex.ParamName == "comparer")
{
    var top = source.MaxBy(keySelector); // default comparer
}

Prevention

When it happens

Trigger: Calling source.MaxBy(keySelector, null), or passing a comparer obtained from DI/configuration/factory that returned null, e.g. items.MaxBy(x => x.Key, registry.GetComparer<K>()) with an unregistered type.

Common situations: Comparer registries or strategy maps missing an entry for the key type; generic code forwarding an optional comparer parameter left null by the caller.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/7ba71b6f5f1353b7. Report an issue: GitHub.

Appendix: source

Thrown at Ix.NET/Source/System.Interactive/System/Linq/Operators/MaxBy.cs:48

        /// <summary>
        /// Returns the elements with the minimum key value by using the specified comparer to compare key values.
        /// </summary>
        /// <typeparam name="TSource">Source sequence element type.</typeparam>
        /// <typeparam name="TKey">Key type.</typeparam>
        /// <param name="source">Source sequence.</param>
        /// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
        /// <param name="comparer">Comparer used to determine the maximum key value.</param>
        /// <returns>List with the elements that share the same maximum key value.</returns>
        [Obsolete("Use MaxByWithTies to maintain same behavior with .NET 6 and later", false)]
        public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

            return ExtremaBy(source, keySelector, (key, minValue) => comparer.Compare(key, minValue));
        }
#endif
    }
}

View on GitHub (pinned to 94b5d5ab91)