It may be obvious to some readers, however I was a little bit surprised when I discovered that. Actually, I realized this by looking at a non-trivial class hierarchy in real world application. One can easily think that discussion about inheritance is kind of theoretical area and it primarily appears during job interviews, but it is not true. I will explain real use case and real reasoning behind this hierarchy later in this post, now please take a look at the following program. Generally, the point is that 1) we have to use reference of an interface type and we want more than one specialized implementations of the interface 2) we need to have class B
inherit from class A
. Without the second requirement it would be obvious: it would be sufficient just to write two separate implementations of IActivity
and we are done.
It prints A does activity
despite ia
variable storing reference to an object of type B
and also despite explicitly declaring hiding of base method. It was not clear to me why is it so. It is obvious that the type B
has its own implementation, so why is it not run here? To overcome this I initially created base class declared as abstract:
It prints B does activity
, but it also is overcomplicated. Then I came up with simpler solution — it turns out we have to explicitly mark class B
as implementing IActivity
.
It prints B does activity
, but it is not perfect. Method hiding is not a good practice. Finally I ended up with more elegant (and the simplest, I guess) solution:
In here we are using virtual
and override
modifiers to clearly specify an intention of specializing the method. It is better than previous one, because just by looking at class A
we are already informed that further specialization is going to occur.
The real usage scenario is that we have an interface representing Entity Framework Core context. We have two distinct implementations: one uses real database and the other uses in-memory database for tests. The latter inherits from the former because what inheritance is all about is copying code. We just want to have the same methods for in-memory implementation like for regular one, but with some slight modifications e.g. in methods executing raw SQL. We also have to use an interface to refer to these objects, because this is how dependency injection containers work.
As you can see, what might seem purely theoretical, actually is used in real world line of business application. Although I have been pretty confident I understand the principles of inheritance in object oriented programming since my undergrad computer science lectures, but as I mentioned, I was surprised discovering that we have to explicitly put : IActivity
on class B
. The implementation has already been there. Anyway, this example encourages me to be always prepared to verify assumptions I make.