I'm having a class design dilemma: I have a generic base class , and multiple derived classes. The base is a CRTP class that provides common functionality, some of which requires inherited-class-specific data. Therefore, the base class is abstract and the child classes must implement certain "provider" methods. For example:
public abstract class Base<T> where T : Base<T>, new() {
private static T reference = new T();
public static int Foo => reference.GetFoo();
public static string Bar => reference.GetBar();
protected abstract int GetFoo();
protected abstract string GetBar();
// ...
}
The problem is that the information is not instance-specific, and should be accessible from a generic parameter. So using the example above, this method could be in another class:
public void ComputeSomething<T>() where T : Base<T>, new() {
int foo = Base<T>.Foo;
// ...
}
Or if the child class is known: ChildClass.Foo
This works, but overall the solution feels "dirty", as I hate to clutter every instance with type information, and the base class must keep a reference
instance (How a sub class can be created in its base class still bends my brain a bit, and generally seems like a bad idea). I would put the information in a cache or factory or something, but I'm not in control of all the child classes, so the system has to be expandable. I looked at using attributes, but there's no way (that I know of) to enforce certain attributes be present at compile time. Really, I feel like I need static interfaces or static abstract members, but C# doesn't have those.
So my question is: how is this kind of problem generally solved?
I'm starting to think I need a separate provider class or something
That's the answer. Whenever I get into these complicated generic inheritance scenarios, the problem is that I'm combining classes through inheritance rather than by composition. That's why you'll hear the principle "favor composition over inheritance."
Whatever the derived classes need from the base class, does it really need to obtained through inheritance, or can it be obtained by creating a separate class and depending on it?
If multiple derived classes are going to depend on the same class (which suggests a case for inheritance) then perhaps that separate class dependency can be built into the base class. But it's still a separate class. The derived classes inherit the dependency on it rather than inheriting the actual methods and properties.
What I've found is that inheritance more often works when it's the result of refactoring, perhaps a way to refactor duplicate code (or stop it before it starts.) When I begin with it thinking that I'm going to reuse code up front it more likely ends up getting too convoluted. Perhaps it's because we're really still writing the classes and determining how they're going to work. Trying to build a hierarchy forces us to assume too much up front, and then it's frustrating to abandon the design. It works better when refactoring in hindsight or when we're considering adding new classes that might duplicate code.
But even in the case where I'm adding new classes and don't want to duplicate code I might consider refactoring the duplicate code into its own class rather than having the new class inherit from the existing one.