Polymorphism means one name, many forms.
So how does it help us?#Let us consider the same example given in the inheritance example.
Let us say that you are writing a serialization component. It can either write the ouput to MemoryStream or NetworkStream or FileStream. If there is no inheritance you’ll end up writing this Serialization component for all the three cases. That’ll by quite some duplication. Since you have a base class Stream, you can use the base class variable to open, write data and close the stream after the serialization. Your method can take a prameter of type Base class Stream. If you call Open or Write or Close it is dispatched to the actual dervied type passed to the serialize method. The class to dispatch is not decided at compile time based on the variable type (The call has to be dispatched to the Stream class then). Instead the method to which the call is dispatched is decided based on what is the type of the instance passed to the method at runtime. The same open call is dispatched to the NetworkStream if the Stream parameter holds a NetworkStream instance or a FileStream if the Stream parameter holds a FileStream instance. Polymorphism provides an opportunity tremendous amount of reuse and extensibility. How does it help in extensibility?
Assume you add an EncryptedStream tomorrow you still need not change the Serialize methods as long as your EncryptedStream class inherits from the Stream class. Thus Polymorphism allows your code to work with classes that you do not know at the time of writing your piece of code. This is what which provides extensibility. What we discussed is runtime Polymorphism. Method overloading / operator overloading is an example of Compile time Polymorphism where the dispatch to the method is decided at compile time. Another classification is interface based polymorphic dispatch versus inheritance based polymorphic dispatch.