dotnet/reactive · error · ArgumentNullException

ArgumentNullException: conversion

Error message

ArgumentNullException: conversion

What it means

FromEventPattern<TDelegate, TSender, TResult>(Func<TypedEventHandler<TSender, TResult>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler) supports custom event-handler delegate types. It validates in order and throws ArgumentNullException("conversion") when the conversion function is null. The conversion maps the internal TypedEventHandler to the event's native delegate type and is essential for subscribe/unsubscribe.

Solutions

  1. Supply a real conversion, e.g. h => new SomeEventHandler(h) (or a lambda matching the delegate's signature).
  2. If the delegate matches TypedEventHandler exactly, use the simpler two-delegate overload and avoid conversion entirely.
  3. In generic wrappers, validate the conversion argument before forwarding to Rx and throw a caller-specific error.

Example fix

// before
Observable.FromEventPattern<EventHandler, Sender, Result>(
    null, // throws
    h => src.Ev += h,
    h => src.Ev -= h);
// after
Observable.FromEventPattern<EventHandler, Sender, Result>(
    h => (Sender s, Result r) => h(s, r) is implicit — supply explicit:
    h => new EventHandler((s, r) => h(s, r)),
    h => src.Ev += h,
    h => src.Ev -= h);
Defensive patterns

Strategy: validation

Validate before calling

if (conversion == null)
    throw new InvalidOperationException("conversion delegate required for custom event handler types");
if (addHandler == null || removeHandler == null)
    throw new InvalidOperationException("add/remove handlers required");

Type guard

bool ValidConversion<TD, TS, TR>(Func<TypedEventHandler<TS, TR>, TD>? c) => c is not null;

Try / catch

try { obs = Observable.FromEventPattern<TDelegate, TSender, TResult>(conv, add, remove); }
catch (ArgumentNullException ex) when (ex.ParamName == "conversion") { log.Error("conversion missing; use simple overload or provide factory for delegate type"); }

Prevention

When it happens

Trigger: Calling the three-generic overload with conversion == null, e.g. FromEventPattern<RoutedEventHandler, S, R>(null, add, remove), or supplying a conversion variable that wasn't initialized.

Common situations: Generic helper methods forwarding a conversion parameter that callers pass as null; attempts to use the simple overload's pattern with a delegate type that requires explicit conversion; ported code from .NET event idioms where conversion was considered optional.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive.WindowsRuntime/System.Reactive.Linq/WindowsObservable.Events.cs:73

        }

        /// <summary>
        /// Converts a typed event, conforming to the standard event pattern, to an observable sequence.
        /// </summary>
        /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
        /// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
        /// <typeparam name="TResult">The type of the event data generated by the event.</typeparam>
        /// <param name="conversion">A function used to convert the given event handler to a delegate compatible with the underlying typed event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
        /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
        /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
        /// <returns>The observable sequence that contains data representations of invocations of the underlying typed event.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
        /// <seealso cref="ToEventPattern"/>
        public static IObservable<EventPattern<TSender, TResult>> FromEventPattern<TDelegate, TSender, TResult>(Func<TypedEventHandler<TSender, TResult>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
        {
            if (conversion == null)
            {
                throw new ArgumentNullException(nameof(conversion));
            }

            if (addHandler == null)
            {
                throw new ArgumentNullException(nameof(addHandler));
            }

            if (removeHandler == null)
            {
                throw new ArgumentNullException(nameof(removeHandler));
            }

            return Observable.Create<EventPattern<TSender, TResult>>(observer =>
            {
                var h = conversion(new TypedEventHandler<TSender, TResult>((sender, args) =>
                {
                    observer.OnNext(new EventPattern<TSender, TResult>(sender, args));
                }));

View on GitHub (pinned to 94b5d5ab91)