stride3d/stride · error · InvalidOperationException

The generic argument of this class must be the type being…

Error message

The generic argument of this class must be the type being implemented.

What it means

ValueConverterBase<T> enforces the CRTP pattern: the generic argument T must be the concrete class deriving from it. The protected constructor compares GetType() with typeof(T) and throws InvalidOperationException when they differ, ensuring the singleton ProvideValue pattern (which news up a T) returns an instance of the actual converter class.

Solutions

  1. Set the generic argument to the class being declared: class MyConverter : ValueConverterBase<MyConverter>
  2. If you need a shared intermediate base, make it abstract and have each leaf pass itself: abstract class ConverterBase : ValueConverterBase<ConverterBase> is invalid — each concrete class must use its own type
  3. Verify every class deriving from ValueConverterBase<T> passes its own name as T

Example fix

// before
class MyConverter : ValueConverterBase<XOrMultiConverter> { ... }
// after
class MyConverter : ValueConverterBase<MyConverter> { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof(T) != GetType()) throw new InvalidOperationException("T must equal the implementing type");

Type guard

bool IsValidConverterBase<T>(object o) => o.GetType() == typeof(T);

Try / catch

try { var c = new MyConverter(); } catch (InvalidOperationException ex) when (ex.Message.Contains("generic argument")) { /* fix T */ }

Prevention

When it happens

Trigger: Declaring a converter like class MyConverter : ValueConverterBase<SomeOtherConverter>, or subclassing an intermediate base without re-pointing T to the leaf class.

Common situations: Copy-pasting a converter declaration and forgetting to update the generic argument; creating an intermediate abstract base class that keeps the wrong T; renaming a converter class without updating the generic parameter.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/966e26db2f02efd1. Report an issue: GitHub.

Appendix: source

Thrown at sources/presentation/Stride.Core.Presentation.Wpf/ValueConverters/ValueConverterBase.cs:25

using Stride.Core.Annotations;

namespace Stride.Core.Presentation.ValueConverters
{
    /// <summary>
    /// An abstract class for implementations of <see cref="IValueConverter"/> that supports markup extensions.
    /// </summary>
    /// <typeparam name="T">The type of <see cref="IValueConverter"/> being implemented.</typeparam>
    public abstract class ValueConverterBase<T> : MarkupExtension, IValueConverter where T : class, IValueConverter, new()
    {
        private static T valueConverterInstance;

        /// <summary>
        /// Initializes a new instance of the <see cref="ValueConverterBase{T}"/> class.
        /// </summary>
        /// <exception cref="InvalidOperationException">The generic argument does not match the type of the implementation of this class.</exception>
        protected ValueConverterBase()
        {
            if (GetType() != typeof(T)) throw new InvalidOperationException("The generic argument of this class must be the type being implemented.");
        }

        /// <inheritdoc/>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return valueConverterInstance ?? (valueConverterInstance = new T());
        }

        /// <inheritdoc/>
        public abstract object Convert(object value, [NotNull] Type targetType, object parameter, CultureInfo culture);

        /// <inheritdoc/>
        public abstract object ConvertBack(object value, [NotNull] Type targetType, object parameter, CultureInfo culture);
    }
}

View on GitHub (pinned to 96fad776d2)