I am working on a mini-framework for "runnable" things. (They are experiments, tests, tasks, etc.)
// Something that "runs" (in some coordinated way) multiple "runnable" things.
interface IRunnableOf<T> where : IRunnable
// Provide base-class functionality for a "runner"
abstract class RunnerBase<T> : IRunnableOf<T>
class SequentialRunner<T> : RunnerBase<T> // Same interface, different behavior.
class ConcurrentRunner<T> : RunnerBase<T>
// other types of runners.
class ConcurrentBlockRunner : SequentialRunner<Block>
class SequentialBlockRunner : ConcurrentRunner<Block>
Now, how can I reconcile ConcurrentBlockRunner
and SequentialBlockRunner
? By this I mean:
Refer to them by a common ancestor, for use in a collection. (IEnuerable<T>
where T = ??)
Provide additional base class functionality. (Add a property, for example).
I remedied #1 by adding another interface that just specified a type parameter to IA<T>
:
interface IBlockRunner : IRunnableOf<Block> { }
And modified my ConcurrentBlockRunner
and SequentialBlockRunner
definitions to be:
class ConcurrentBlockRunner : SequentialRunner<Block>, IBlockRunner
class SequentialBlockRunner : ConcurrentRunner<Block>, IBlockRunner
Since ConcurrentBlockRunner
and SequentialBlockRunner
both use Block
for their type parameter, this seems to be a correct solution. However, I can't help but feel "weird" about it, because well, I just tacked that interface on.
For #2, I want to add a couple pieces of common data to ConcurrentBlockRunner
and SequentialBlockRunner
. There are several properties that apply to them, but not to their only common base class, which is all the way up at RunnerBase<T>
.
This is the first time while using C# that I've felt multiple inheritance would help. If I could do:
abstract class BlockRunnerBase {
int Prop1 { get; set; }
int Prop2 { get; set; }
class ConcurrentBlockRunner : SequentialRunner<Block>, BlockRunnerBase
class SequentialBlockRunner : ConcurrentRunner<Block>, BlockRunnerBase
Then I could simply add these extra properties to BlockRunnerBase, and everything would just work. Is there a better way?
I know I will be recommended immediately to consider composition, which I began to work with:
class BlockRunner : IBlockRunner {
IBlockRunner _member;
int Prop1 { get; set; } // Wish I could put these in some base class
int Prop2 { get; set; }
// Lots of proxy calls, and proxy events into _member
void Method() { _member.Method(); }
event SomeEvent
{
add { _member.SomeEvent += value; }
remove { _member.SomeEvent -= value; }
}
}
The problem I encountered (driving me to write this question) was that once you compose, you lose type compatibility. In my case, _member was firing an event, so the sender
parameter was of type SequentialBlockRunner
. However, the event handler was trying to cast it to type BlockRunner
, which of course failed. The solution there is not use add
/remove
to proxy the events, but actually handle them, and raise an event of my own. So much work just to add a couple properties...
The only way to implement multiple inheritance is to implement multiple interfaces in a class. In java, one class can implements two or more interfaces. This also does not cause any ambiguity because all methods declared in interfaces are implemented in class.
Composition and Interface Inheritance are the usual alternatives to classical multiple inheritance. Everything that you described above that starts with the word "can" is a capability that can be represented with an interface, as in ICanBuild , or ICanFarm .
C# does not support multiple inheritance , because they reasoned that adding multiple inheritance added too much complexity to C# while providing too little benefit. In C#, the classes are only allowed to inherit from a single parent class, which is called single inheritance .
Allowing multiple inheritance makes the rules about function overloads and virtual dispatch decidedly more tricky, as well as the language implementation around object layouts. These impact language designers/implementors quite a bit and raise the already high bar to get a language done, stable, and adopted.
Composition over Inheritance, FTW!
To be more explicit:
class SequentialRunner<T> : RunnerBase<T>
should implement IRunnableOf<T>
and proxy the RunnerBase<T>
without inheriting it.
class SequentialRunner<T> : IRunnableOf<T>
{
private readonly RunnerBase<T> _runnerBase;
...
}
You can use extension methods to create mixin-like constructs, even with property-like elements.
I've also created an experiment with trait-like constructs in C#, NRoles.
But, all of these require non-standard coding, and will not be ideal for APIs that are meant to be exposed to third parties. I think you should try to rearrange your classes and use composition with delegation using interfaces if possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With