{"record":{"id":"966e26db2f02efd1","repo":"stride3d/stride","slug":"the-generic-argument-of-this-class-must-be-the-type-being-966e26","errorCode":null,"errorMessage":"The generic argument of this class must be the type being implemented.","messagePattern":"The generic argument of this class must be the type being implemented\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/presentation/Stride.Core.Presentation.Wpf/ValueConverters/ValueConverterBase.cs","lineNumber":25,"sourceCode":"using Stride.Core.Annotations;\n\nnamespace Stride.Core.Presentation.ValueConverters\n{\n    /// <summary>\n    /// An abstract class for implementations of <see cref=\"IValueConverter\"/> that supports markup extensions.\n    /// </summary>\n    /// <typeparam name=\"T\">The type of <see cref=\"IValueConverter\"/> being implemented.</typeparam>\n    public abstract class ValueConverterBase<T> : MarkupExtension, IValueConverter where T : class, IValueConverter, new()\n    {\n        private static T valueConverterInstance;\n\n        /// <summary>\n        /// Initializes a new instance of the <see cref=\"ValueConverterBase{T}\"/> class.\n        /// </summary>\n        /// <exception cref=\"InvalidOperationException\">The generic argument does not match the type of the implementation of this class.</exception>\n        protected ValueConverterBase()\n        {\n            if (GetType() != typeof(T)) throw new InvalidOperationException(\"The generic argument of this class must be the type being implemented.\");\n        }\n\n        /// <inheritdoc/>\n        public override object ProvideValue(IServiceProvider serviceProvider)\n        {\n            return valueConverterInstance ?? (valueConverterInstance = new T());\n        }\n\n        /// <inheritdoc/>\n        public abstract object Convert(object value, [NotNull] Type targetType, object parameter, CultureInfo culture);\n\n        /// <inheritdoc/>\n        public abstract object ConvertBack(object value, [NotNull] Type targetType, object parameter, CultureInfo culture);\n    }\n}\n","sourceCodeStart":7,"sourceCodeEnd":41,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/presentation/Stride.Core.Presentation.Wpf/ValueConverters/ValueConverterBase.cs#L7-L41","documentation":"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.","triggerScenarios":"Declaring a converter like class MyConverter : ValueConverterBase<SomeOtherConverter>, or subclassing an intermediate base without re-pointing T to the leaf class.","commonSituations":"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.","solutions":["Set the generic argument to the class being declared: class MyConverter : ValueConverterBase<MyConverter>","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","Verify every class deriving from ValueConverterBase<T> passes its own name as T"],"exampleFix":"// before\nclass MyConverter : ValueConverterBase<XOrMultiConverter> { ... }\n// after\nclass MyConverter : ValueConverterBase<MyConverter> { ... }","handlingStrategy":"type-guard","validationCode":"if (typeof(T) != GetType()) throw new InvalidOperationException(\"T must equal the implementing type\");","typeGuard":"bool IsValidConverterBase<T>(object o) => o.GetType() == typeof(T);","tryCatchPattern":"try { var c = new MyConverter(); } catch (InvalidOperationException ex) when (ex.Message.Contains(\"generic argument\")) { /* fix T */ }","preventionTips":["Always pass the declaring class itself as T","Avoid intermediate non-abstract generic bases that hard-code T","Review converter declarations after renames"],"tags":["wpf","generics","value-converter"],"backgroundTag":"type-mismatch","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}