Is it possible to override a method implementation by deriving from an interface which gives a default implementation for that method? If not, how should I restructure my code to avoid explicit method calls to the interface's implementation?
Here is an overview of the structure of classes in my project:
interface IInputProcessor
{
public ProcessInputsResult ProcessInputs(Item[] inputs);
}
abstract class MachineComponent : IInputProcessor
{
public abstract ProcessInputsResult ProcessInputs(Item[] inputs);
}
interface IComponentContainer<T> where T : MachineComponent
{
public T[] Components { get; }
}
class MachineSystem : MachineComponent, IComponentContainer<MachineComponent>
{
public MachineComponent[] Components { get; }
}
My goal is to provide an implementation for ProcessInputs
inside the IComponentContainer
interface, since all component containers must process the inputs of each of their components, and the implementation for this can be the shared across the deriving classes.
To achieve this, I tried creating a default method implementation for ProcessInputs
inside IComponentContainer
, like so:
interface IComponentContainer<T> : IInputProcessor where T : MachineComponent
{
public T[] Components { get; }
ProcessInputsResult IInputProcessor.ProcessInputs(Item[] inputs)
{
...
}
}
Notice how I also made IComponentContainer
derive from IInputProcessor
, since all component containers have to process inputs for all their components.
I was hoping that this new definition would mean that MachineSystem
, which is both a MachineComponent
and a IComponentContainer<MachineComponent>
, would not need to specify an implementation for MachineComponent.ProcessInputs
, since this is given in the IComponentContainer
interface.
This was not the case, and I had to explicitely refer the method to the implementation given in the interface like this:
class MachineSystem : MachineComponent, IComponentContainer<MachineComponent>
{
...
public override ProcessInputsResult ProcessInputs(Item[] inputs) =>
((IComponentContainer<MachineComponent>)this).ProcessInputs(inputs);
}
I think this defeats the purpose of providing the default implementation in the interface as deriving classes have to specify that they want to use it.
Is there a good solution for this problem, or should I change the structure of the code?
UPDATE: In response to Ivan Petrov's answer
interface IComponentContainer<T> : IInputProcessor where T : MachineComponent {
public T[] Components { get; }
}
class ComponentContainer<T> : MachineComponent, IComponentContainer<T>
where T : MachineComponent {
public T[] Components { get; }
public override ProcessInputsResult ProcessInputs(Item[] inputs) {
// implementation will serve for interface method too
}
}
class MachineSystem : ComponentContainer<MachineComponent> {
// we can still override ProcessInputsResult here too
}
Using a shared base class for the implementation of component container processing wouldn't work in my situation because the classes implementing IComponentContainer
, all deriving from the abstract class MachineComponent
, are at different levels in the class hierarchy. For example, MachineSystem : IComponentContainer<MachineComponent>
inherits MachineComponent
directly, whereas CompositeComponent : IComponentContainer<SimpleComponent>
inherits another class SimpleComponent
which in turn is derived from MachineComponent
. For this reason using a single base class that all of these derive from would impose the same hierarchy on all of them. This could be resolved easily if generic inheritance types were possible, like this:
class ComponentContainer<TContainer, TBase> : TBase, IComponentContainer<TContainer>
where TContainer : MachineComponent
where TBase : MachineComponent
{
public T[] Components { get; }
public override ProcessInputsResult ProcessInputs(Item[] inputs)
{
...
}
}
But since this is not supported I don't see a way to make it work with a shared base class.
public override ProcessInputsResult ProcessInputs(Item[] inputs) => ((IComponentContainer<MachineComponent>)this).ProcessInputs(inputs);