I have a need where I would like to have a base class only available to one specific subclass. This is due to a limitation where you cannot define an Attribute
as an inner class of a generic.
The reason we want to define the attribute within the generic is when you do that and set its type to protected
, that attribute is now only available to subclasses of your base class, keeping your API clean.
As an example, consider the abstract class ExampleMarkupExtension<T>
that inherits from MarkupExtension
. I want to define the attribute StaticInfoAttribute
as an inner class, so it is only available to subclasses of ExampleMarkupExtension<T>
.
However, as mentioned, you can't define an attribute as an inner class of a generic, so my workaround is to create a second, non-generic ExampleMarkupExtensionBase
class which inherits from MarkupExtension
, define StaticInfoAttribute
as an inner class there, then have the generic inherit from that class instead.
The issue is now I have an extra class in my hierarchy that anyone can subclass. While it's technically harmless, I like keeping my API surface area clean.
ExampleMarkupExtensionBase
: Non-generic abstract base classThis is the class that defines the attribute which should only be available to subclasses of this class. This class only exists because I can't put StaticInfoAttribute
within the generic abstract base class ExampleMarkupExtension<T>
below.
public abstract class ExampleMarkupExtensionBase : MarkupExtension {
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)]
protected class StaticInfoAttribute : Attribute {
public StaticInfoAttribute(string value) => Value = value;
public readonly string Value;
}
}
ExampleMarkupExtension<T>
: Generic abstract base classThis is the class that holds the static members which are unique to the subclasses of this class. It does this by using the subclass as the type parameter for the base class (see the where
clause). The value of the static field is set from the attribute, hence it only being of any use for this specific subclass of ExampleMarkupExtension
above.
public abstract class ExampleMarkupExtension<T> : ExampleMarkupExtensionBase
where T : ExampleMarkupExtension<T> {
static ExampleMarkupExtension() => Value = typeof(T).GetRequiredCustomAttribute<StaticInfoAttribute>().Value;
public static readonly string Value;
public sealed override object ProvideValue(IServiceProvider serviceProvider)
=> Value;
}
Some may ask what's the harm of leaving it as-is. Technically, there's nothing wrong with letting a person subclass ExampleMarkupExtensionBase
. There's just no point to it.
For that matter, there's technically no harm with moving the StaticInfoAttribute
outside of the class as well, removing the need for the non-generic base class entirely. I just chose the former approach as it's more important to restrict the attribute's usage than it is to stop someone from subclassing the base class.
As per our comments, you can use the EditorBrowsable
attribute to hide the method from projects that reference it. Note however it just hides it from IntelliSense and if a user was to type out ExampleMarkupExtensionBase
that the class still exists and is valid to be used.
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class ExampleMarkupExtensionBase : MarkupExtension