{"record":{"id":"8f24bfb20a7f88a7","repo":"dotnet/reactive","slug":"comparer-observable-aggregates","errorCode":null,"errorMessage":"comparer","messagePattern":"comparer","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Linq/Observable.Aggregates.cs","lineNumber":663,"sourceCode":"        /// Determines whether an observable sequence contains a specified element by using a specified System.Collections.Generic.IEqualityComparer{T}.\n        /// </summary>\n        /// <typeparam name=\"TSource\">The type of the elements in the source sequence.</typeparam>\n        /// <param name=\"source\">An observable sequence in which to locate a value.</param>\n        /// <param name=\"value\">The value to locate in the source sequence.</param>\n        /// <param name=\"comparer\">An equality comparer to compare elements.</param>\n        /// <returns>An observable sequence containing a single element determining whether the source sequence contains an element that has the specified value.</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"source\"/> or <paramref name=\"comparer\"/> is null.</exception>\n        /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>\n        public static IObservable<bool> Contains<TSource>(this IObservable<TSource> source, TSource value, IEqualityComparer<TSource> comparer)\n        {\n            if (source == null)\n            {\n                throw new ArgumentNullException(nameof(source));\n            }\n\n            if (comparer == null)\n            {\n                throw new ArgumentNullException(nameof(comparer));\n            }\n\n            return s_impl.Contains(source, value, comparer);\n        }\n\n        #endregion\n\n        #region + Count +\n\n        /// <summary>\n        /// Returns an observable sequence containing an <see cref=\"int\" /> that represents the total number of elements in an observable sequence.\n        /// </summary>\n        /// <typeparam name=\"TSource\">The type of the elements in the source sequence.</typeparam>\n        /// <param name=\"source\">An observable sequence that contains elements to be counted.</param>\n        /// <returns>An observable sequence containing a single element with the number of elements in the input sequence.</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"source\"/> is null.</exception>\n        /// <exception cref=\"OverflowException\">(Asynchronous) The number of elements in the source sequence is larger than <see cref=\"long.MaxValue\"/>.</exception>\n        /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>","sourceCodeStart":645,"sourceCodeEnd":681,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Linq/Observable.Aggregates.cs#L645-L681","documentation":"System.Reactive throws ArgumentNullException immediately when the IEqualityComparer<TSource> passed to Contains is null. The comparer is mandatory on this overload because equality of TSource is undefined without it; the null check runs synchronously at the call site before delegating to the implementation. Passing null here instead of using the comparer-less overload is a common mistake.","triggerScenarios":"Calling source.Contains(value, null) when intending the default-equality overload, or passing a comparer variable resolved from configuration/DI that was not registered.","commonSituations":"Generic helper code that always forwards a comparer parameter which is null in some paths; config-driven comparer selection failing to match a known name; refactors that added the comparer overload without branching on null.","solutions":["Call the comparer-less overload source.Contains(value) when default EqualityComparer<T>.Default is desired","Pass EqualityComparer<TSource>.Default explicitly instead of null","Guard the comparer before calling: comparer != null ? source.Contains(v, comparer) : source.Contains(v)","Fix the resolver that produced the null comparer and register/choose a valid one"],"exampleFix":"// before\nvar has = names.Contains(\"alice\", (IEqualityComparer<string>)null);\n// after\nvar has = names.Contains(\"alice\", StringComparer.OrdinalIgnoreCase);\n// or for default equality:\nvar has = names.Contains(\"alice\");","handlingStrategy":"validation","validationCode":"var has = comparer != null\n    ? source.Contains(value, comparer)\n    : source.Contains(value);","typeGuard":"bool HasComparer<T>(IEqualityComparer<T> comparer) => comparer != null || EqualityComparer<T>.Default != null;","tryCatchPattern":"try\n{\n    var has = source.Contains(value, comparer);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == \"comparer\")\n{\n    // fall back to default equality\n    var has2 = source.Contains(value);\n}","preventionTips":["Use the comparer-less Contains overload when default equality suffices","Pass EqualityComparer<T>.Default instead of null for generic forwarding code","Make config-driven comparer lookup return a default rather than null","Never forward an optional comparer parameter directly into Rx operators without a null branch"],"tags":["rx","null-argument","argument-validation","comparer"],"backgroundTag":"null-argument","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}