First of all, forget about "overloading". It has nothing to do with OOP. To finish with this topic: this is nothing but the syntax allowing different methods of the same type to have identical names; they are still different (formally speaking, totally unrelated methods) which should be resolved by a compiler (so, no dynamic dispatch, ever) by the actual call parameters at the point of call, whenever possible, otherwise a compilation error is generated. This name is very unfortunate: nothing is actually "overloaded" just because nothing is "loaded; and this naming for such a simple syntactic feature created enormous amount of confusion in the beginners.
Now, many developers really overlook the fact that polymorphism without overriding (which is the basis of dynamic call dispatch in "classical" OOP, the real heart of OOP) is actually possible. This is a very different form of polymorphism, based on
interfaces. This kind of polymorphism provides more flexibility than "classical" virtual-based polymorphism, but it creates some more problems of the creation of the types representing and some potential problem for code reuse. One of the most overlooked feature of it is: polymorphic type representing the elements don't even have to be classes!
First of all, please see my past answer, with references to some previous answers:
Doubts on Interfaces[
^].
Let's demonstrate it:
interface IOnePolymorphicSetMember {
void SomeFeature();
}
interface IAnotherPolymorphicSetMember {
void SomeOtherFeature();
}
class OnePolymorphicSet {
internal void Add(IOnePolymorphicSetMember element) {
list.Add(element);
}
internal void ShowAll() {
foreach (IOnePolymorphicSetMember element in list)
element.SomeFeature();
}
System.Collections.Generic.IList<IOnePolymorphicSetMember> list =
new System.Collections.Generic.List<IOnePolymorphicSetMember>();
}
class AnotherPolymorphicSet {
internal void Add(IAnotherPolymorphicSetMember element) {
list.Add(element);
}
internal void ShowAll() {
foreach (IAnotherPolymorphicSetMember element in list)
element.SomeOtherFeature();
}
System.Collections.Generic.IList<IAnotherPolymorphicSetMember> list =
new System.Collections.Generic.List<IAnotherPolymorphicSetMember>();
}
It may seem that two interfaces and two classes are well too similar and some code reuse is missing, so generics could be used, but this is not so: I put so similar code just for demonstration. In reality life, interfaces are very different and method have different signatures, so no further abstraction can be applied.
Now, we can demonstrate a couple of wonderful features. First, the elements could be classes or not. Structures, being value types, nevertheless, also can implement interfaces and be added to the polymorphic sets/containers. Here is how:
class OnePolymorphicElement : IOnePolymorphicSetMember {
void IOnePolymorphicSetMember.SomeFeature() {
System.Console.WriteLine("{0}: {1}", this.GetType().FullName, this);
}
}
struct AnotherPolymorphicElement : IAnotherPolymorphicSetMember {
void IAnotherPolymorphicSetMember.SomeOtherFeature() {
System.Console.WriteLine("{0}: {1} ({2})", this.GetType().Name, this.Name, this.Id);
}
internal string Name { get; set; }
internal string Id { get; set; }
}
And another wonderful feature is that classes and structures implementing interfaces can be related by they inheritance relationships or not, related to each other in any other way or not, and, essentially, the same object can participate in different, possible unrelated polymorphous sets. Here is how:
struct CombinedPolymorphicElement : IOnePolymorphicSetMember, IAnotherPolymorphicSetMember {
void IOnePolymorphicSetMember.SomeFeature() {
System.Console.WriteLine("{0}: {1}", this.GetType().FullName, this);
}
void IAnotherPolymorphicSetMember.SomeOtherFeature() {
System.Console.WriteLine("{0}: {1} ({2})", this.GetType().Name, this.Name, this.Id);
}
internal string Name { get; set; }
internal string Id { get; set; }
}
The instance of the structure defined below can be added to two different sets/containers at the same time, through, according to our code sample, both
OnePolymorphicSet.Add
and
AnotherPolymorphicSet.Add
.
Note that we also have "enforced" level of abstraction: not only the set is
agnostic to a concrete
runtime type of the contained objects, but it is even agnostic to the nature of those types: they can be either reference types (classes) or value types (structures); actually, from the point of view of the set/container, all objects are references.
—SA