SoFunction
Updated on 2025-03-08

Implementation methods of polymorphic phenomena and polymorphism in C#

This article describes the implementation methods of polymorphic phenomena and polymorphism in C#. Share it for your reference. The specific analysis is as follows:

Object-oriented feature encapsulation, inheritance, and polymorphism. Polymorphism (polymorphism) comes from Greek words, referring to "multiple forms". An important feature of polymorphism is that the call to the method is determined at runtime rather than at compile time. Keywords used to implement polymorphism in .NET include virtual, override, abstract, and interface.

1. Virtual realizes polymorphism
 
The shape class is a general base class, and draw is a virtual method. Each derived class can have its own override version. At runtime, the draw method can be called dynamically with the variables of the shape class.

public class Shape
{
    public virtual void Draw()
    {
      ("base class drawing");
    }
}
public class Rectangle :Shape
{
    public override void Draw()
    {
      ("Drawing a Rectangle");
    }
}
public class Square :Rectangle
{
    public override void Draw()
    {
      ("Drawing a Square");
      ();
    }
}
class Program
{
    static void Main(string[]args)
    {
      <Shape> shapes =new List<Shape>();
      (new Rectangle());
      (new Square());
      foreach(Shape s in shapes)
      {
        ();
      }
      ();
      /*Run result
        Drawing a Rectangle
        Drawing a Square
        Drawing a Rectangle
        */
    }
}

Methods, properties, events, and indexers can all be modified by virtual, but fields cannot. The derived class must use override to indicate that the class members participate in virtual calls. If the draw method in Square is replaced with new modification, it means that the draw method does not participate in virtual calls, and is a new method, but the name and base class method are duplicated.

public new void Draw()
{
      ("Drawing a Square");
      ();
}

This method will not be called in the foreach in the Main method, it is not a virtual method anymore. The program run result after using the new modifier,

/*Run result
 Drawing a Rectangle
 Drawing a Rectangle
 */

If the virtual method does not want square to be expanded after rectangle is expanded, you can add sealed modifier before the method.
as follows

public class Rectangle :Shape
{
    public sealed override voidDraw()
    {
      ("Drawing a Rectangle");
    }
}

 
When a derived class rewrites a virtual member, even if the instance of the derived class is accessed as an instance of the base class or the derived class instance is assigned to the parent class variable for access, the rewrite member of the derived class will still be called, and the code can be changed to the following form.

static void Main(string[] args)
{
      <Shape>shapes =new List<Shape>();
      ((Shape)new Rectangle());
      ((Shape)new Square());
      foreach(Shape s inshapes)
      {
        ();
      }
      ();
      /*Run result
        Drawing a Rectangle
        Drawing a Square
        Drawing a Rectangle
        */
}

2. Abstract implements polymorphism
 
The method of being modified by abstract is virtual by default, but virtual keyword modification cannot occur. Classes modified by abstract can have implemented members, can have their own fields, and can have non-abstract modified methods, but cannot be instantiated because abstract things have no instance corresponding to instances. For example, someone asked us to draw a graph (abstract) but we couldn't draw it, but we could draw a rectangle (specific). Below is a polymorphic version implemented with abstract.

public abstract classShape
{
    public abstract void Draw();
}
public class Rectangle :Shape
{
    public override void Draw()
    {
      ("Drawing a Rectangle");
    }
}
public class Square :Rectangle
{
    public override void Draw()
    {
      ("Drawing a Square");
      ();
    }
}
class Program
{
    static void Main(string[]args)
    {
      <Shape>shapes =new List<Shape>();
      (new Rectangle());
      (new Square());
      foreach(Shape s in shapes)
      {
        ();
      }
      ();
     }
}

The method modified by abstract is also extended with the override keyword in the derived class. You can also use the keyword sealed to prevent the derived classes from being expanded.

Interface implements polymorphism

An interface can be composed of methods, properties, events, indexers, or any combination of these four member types. The interface cannot contain fields. Interface members are public, abstract, and virtual by default. To implement interface members, the corresponding members in the class must be public, non-static, and have the same name and signature as the interface members. Below is the polymorphic version of the interface implementation

public interface IShape
{
    void Draw();
}
public class Rectangle :IShape
{
    public void Draw()
    {
      ("Drawing a Rectangle");
    }
}
public class Square: IShape
{
    public void Draw()
    {
      ("Drawing a Square");
    }
}
class Program
{
    static void Main(string[]args)
    {
      <IShape>shapes =new List<IShape>();
      (new Rectangle());
      (new Square());
      foreach(IShape s inshapes)
      {
        ();
      }
      ();
    }
}

Abstract classes and interfaces

A class can implement unlimited interfaces, but can only be inherited from an abstract (or any other type) class. Classes derived from abstract classes can still implement interfaces. msdn's some suggestions on the selection of interfaces and abstract classes,

If you are expected to create multiple versions of the component, create an abstract class. Abstract classes provide easy and easy ways to control component versions. By updating the base class, all inherited classes are automatically updated with changes. On the other hand, the interface cannot be changed once it is created. If a new version of the interface is required, a completely new interface must be created.

If the created function is used between a wide range of different objects, use the interface. Abstract classes should be mainly used for closely related objects, while interfaces are most suitable for providing common functions for unrelated classes.

If you want to design small and concise function blocks, use the interface. If you want to design large functional units, use abstract classes.

If you want to provide common implemented functionality across all implementations of a component, use abstract classes. Abstract classes allow partial implementation of classes, while interfaces do not contain implementations of any members.

A comprehensive example

public interface IShape
{
    void Draw();
}
public class Shape:IShape
{
    void ()
    {
      ("Shape ()");
    }
    public virtual void Draw()
    {
      ("Shape virtual Draw()");
    }
}
public class Rectangle :Shape,IShape
{
    void ()
    {
      ("Rectangle ()");
    }
    public newvirtual void Draw()
    {
      ("Rectangle virtual Draw()");
    }
}
public class Square :Rectangle
{
    public override void Draw()
    {
      ("Square override Draw()");
    }
}
class Program
{
    static void Main(string[]args)
    {
      Square squre = new Square();
      Rectangle rect = squre;
      Shape shape = squre;
      IShape ishape = squre;
      ();
      ();
      ();
      ();
      ();
    }
}
/*Run result:
 Square override Draw()①
 Square override Draw()②
 Shape virtual Draw()③
 Rectangle ()④
 */

In this program, assign the derived class instance to the parent class variable or interface. No explanation is required for the result ①. Result ②, because the Draw method is a virtual method, the calling rule of the virtual method is to call the override version method closest to the instance variable, and the Draw method in the Square class is a method closest to the instance square. Even if an instance of Square type is assigned to a variable of Rectangle type to access, the method overridden by the Square class is still called. For the result ③, it is also a virtual method call. The draw method in the subclass Rectangle is modified with new, which means that the virtual in the shape class is interrupted here. The subsequent override version in Square is targeting the Draw method in the Rectangle. At this time, the implementation closest to the square instance is the Draw method in the Shape class, because the Draw method in the Shape class does not have an override version and can only call its own virtual version. Result ④, because Rectangle redeclares the implementation interface IShape, interface calls also comply with the virtual method calling rules, calling the nearest implementation, and the implementation in Rectangle is closer to the instance square than the implementation in Shape. The () method in Rectangle is implemented by an explicit interface method. There cannot be any access modifiers for it. It can only be accessed through interface variables. It cannot be modified with virtual or override, nor can it be called by derived types. Only accessed with IShape variables. If there is an implementation of an explicit interface in the type and the interface variable is used, the implementation method of the explicit interface is called by default.

override and method selection

public class Base
{
    public virtual void Write(int num)
    {
      ("int:" + ());
    }
}
public class Derived :Base
{
    public override void Write(int num)
    {
      ("derived:" + ());
    }
    public void Write(double num)
    {
      ("derived double:" + ());
    }
}

I hope this article will be helpful to everyone's C# programming.