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-overload MaxByWithTies(source, keySelector, comparer) throws ArgumentNullException with param name 'comparer' when the IComparer<TKey> is null. Unlike Comparer<TKey>.Default used by the two-argument overload, the explicit comparer must be provided.

Solutions

  1. Pass Comparer<TKey>.Default instead of null when default comparison is desired
  2. Null-check the comparer before the call and fall back to Comparer<TKey>.Default
  3. Fix the factory/lookup that produced the null comparer

Example fix

// before
items.MaxByWithTies(x => x.Score, null);
// after
items.MaxByWithTies(x => x.Score, scoreComparer ?? Comparer<int>.Default);
Defensive patterns

Strategy: validation

Validate before calling

if (comparer is null) comparer = Comparer<TKey>.Default;
var ties = source.MaxByWithTies(keySelector, comparer);

Type guard

static IComparer<T> OrDefault<T>(IComparer<T>? c) => c ?? Comparer<T>.Default;

Try / catch

try { result = source.MaxByWithTies(sel, cmp); }
catch (ArgumentNullException ex) when (ex.ParamName == "comparer") { result = source.MaxByWithTies(sel); }

Prevention

When it happens

Trigger: Passing a null IComparer<TKey> in the three-argument overload, e.g. items.MaxByWithTies(x => x.Key, null), commonly when the comparer is fetched from a registry or constructed conditionally.

Common situations: Comparer obtained from a dictionary/config that returned null; a custom comparer class whose factory failed; using null intending 'default' semantics.

Related errors


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

Appendix: source

Thrown at Ix.NET/Source/System.Interactive/System/Linq/Operators/MaxByWithTies.cs:45

        }

        /// <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>
        public static IList<TSource> MaxByWithTies<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));
        }

        private static IList<TSource> ExtremaBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, TKey, int> compare)
        {
            var result = new List<TSource>();

            using (var e = source.GetEnumerator())
            {
                if (!e.MoveNext())
                    throw new InvalidOperationException("Source sequence doesn't contain any elements.");

                var current = e.Current;
                var resKey = keySelector(current);
                result.Add(current);

                while (e.MoveNext())

View on GitHub (pinned to 94b5d5ab91)