C#. Overriding virtual methods in generic classes

C#. Overriding virtual methods in generic classes. Examples

Before exploring this topic, it is recommended that you familiarize yourself with the following topics:


Contents


Search other resources:




1. Overriding virtual methods in generic classes. Features. Syntax

Virtual methods in generic classes can be overridden. The technique of overriding methods in generics works in the same way as in non-generics. More details about overriding methods in classes are described here.

A method that is declared virtual in the base class must be declared with the virtual modifier. Methods of the same name in inherited classes are declared with the override modifier. In turn, inherited classes can be base for other classes. In this case, the chain of override modifiers is continued.

If you do not need a method in a derived class to override a base class method and be considered non-virtual (not supporting polymorphism), then such a method is declared with the new modifier or without the modifier.

If two generic classes that receive the type T as a parameter form a hierarchy, then the general form of declaring a virtual method in the base class is as follows

class Base<T>
{
  ...

  public virtual VirtualMethodName(parameters)
  {
    ...
  }

  ...
}

To provide a mechanism for polymorphism, the derived class declares the method of the same name with the override keyword.

class Derived<T> : Base<T>
{
  ...

  public override VirtualMethodName(parameters)
  {
    ...
  }

  ...
}

 

2. An example that demonstrates overriding methods in generic classes that form a hierarchy. Classes receive type T as a parameter

This example demonstrates a way to override a method of the same name in a derived class in the case of two classes.

The base generic class Base<T> is declared, which receives the type T as a parameter and has the following components:

  • the value field of the generic type T, which defines the class data;
  • constructor with one parameter;
  • virtual accessor methods GetValue(), SetValue(). These methods are declared with the virtual modifier;
  • virtual method PrintValue(), which outputs the value of the internal field value.

It also declares a generic class Derived<T>, which inherits from the Base<T> class. The class contains the following fields and methods:

  • internal field value of generic type T;
  • constructor, calls the base class constructor;
  • virtual methods GetValue() and SetValue(), which override the methods of the same name of the Base<T> class. These methods are declared with the override keyword;
  • virtual method PrintValue(), which overrides the method of the base class of the same name. The method is declared with the virtual modifier.

 

using System;

namespace ConsoleApp1
{
  class Base<T>
  {
    // Internal data
    T value;

    // Constuctor
    public Base(T _value)
    {
      Console.WriteLine("Constructor Base(T)");
      value = _value;
    }

    // Methods of access to value
    public virtual T GetValue()
    {
      Console.WriteLine("Base.GetValue()");
      return value;
    }

    public virtual void SetValue(T _value)
    {
      Console.WriteLine("Base.SetValue()");
      value = _value;
    }

    // A virtual method that outputs the value from the Base class
    public virtual void PrintValue(string message)
    {
      Console.WriteLine(message);
      Console.WriteLine("Base.value = " + value);
    }
  }

  // Derived class that has its own methods
  // whose names are the same as the base class Base<T>
  class Derived<T> : Base<T>
  {
    // Internal field
    T value;

    // Constructor, it must call the base class constructor
    public Derived(T valueBase, T valueDerived) : base(valueBase)
    {
      Console.WriteLine("Constructor Derived(T)");
      value = valueDerived;
    }

    // Access methods - override the access methods of the base class
    public override T GetValue()
    {
      Console.WriteLine("Derived.GetValue()");
      return value;
    }

    public override void SetValue(T _value)
    {
      Console.WriteLine("Derived.SetValue()");
      value = _value;
    }

    // Method that overrides the virtual method of the base class
    public override void PrintValue(string message)
    {
      base.PrintValue(message);
      Console.WriteLine("Derived.value = " + value);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. Create an instance of the base class
      Base<double> obj1 = new Base<double>(2.88);
      obj1.PrintValue("obj1:");

      // Invoke methods of base class
      obj1.SetValue(11.77);
      Console.WriteLine("value = " + obj1.GetValue());
      Console.WriteLine("--------------------");

      // 2. Create an instance of the derived class
      Derived<int> obj2 = new Derived<int>(10, 20);
      obj2.PrintValue("obj2:");

      // Call derived class methods
      obj2.SetValue(35);
      obj2.PrintValue("obj2:");

      Console.ReadKey();
    }
  }
}

The result of the program

Constructor Base(T)
obj1:
Base.value = 2.88
Base.SetValue()
Base.GetValue()
value = 11.77
--------------------
Constructor Base(T)
Constructor Derived(T)
obj2:
Base.value = 10
Derived.value = 20
Derived.SetValue()
obj2:
Base.value = 10
Derived.value = 35

 

3. An example of overriding virtual methods for base and derived classes that receive two types as parameters T1, T2

The example demonstrates:

  • using the inheritance mechanism to declare generic classes that form a hierarchy;
  • the declaration of the base and derived generic classes, which receive parameters of two types T1, T2;
  • calling the constructor of the base class from the inherited class in the case of generics;
  • overriding virtual methods of the base class in order to provide polymorphism;
  • accessing the fields of the base class from the inherited class using the base tool.

Two generic classes are declared, which receive as parameters the types T1, T2. Classes form a hierarchy. One of the classes is base, the other is derived (inherited).

The generalized base class A<T1, T2> contains the following elements:

  • internal fields a1, a2 of types T1, T2 respectively;
  • a constructor with two parameters, which initializes with the values of the fields a1, a2;
  • virtual methods for reading fields a1, a2 named GetA1() and GetA2(). These methods are declared with the virtual modifier;
  • methods for writing new values to fields a1, a2 with the names SetA1(), SetA2(). These methods are declared with the virtual modifier.

The class B<T1, T2> is inherited from the class A<T1, T2>, which does not contain internal fields, but contains the following methods:

  • a constructor with two parameters. This constructor calls the base class constructor using the base keyword;
  • virtual methods for reading the values of the fields a1, a2 of the base class. Methods have the same names as methods of the base class: GetA1(), GetA2(). This means that they override the methods of the base class. To provide polymorphism, methods are declared with the override keyword. These methods call the methods of the base class of the same name using the base keyword;
  • virtual methods SetA1(), SetA2() for writing new values to the fields a1, a2 of the base class. The work of the methods in the class is similar to the GetA1(), GetA2() methods.

Below is the text of the demo program

using System;

namespace ConsoleApp1
{
  class A<T1, T2>
  {
    // Internal data
    T1 a1;
    T2 a2;

    // Constructor
    public A(T1 _a1, T2 _a2)
    {
      a1 = _a1;
      a2 = _a2;
    }

    // Methods of access to a1, a2
    public virtual T1 GetA1()
    {
      return a1;
    }

    public virtual T2 GetA2()
    {
      return a2;
    }

    public virtual void SetA1(T1 a1)
    {
      this.a1 = a1;
    }

    public virtual void SetA2(T2 a2)
    {
      this.a2 = a2;
    }
  }

  // Derived class that has its own methods, the names of which are the same
  // as the names of the base class A<T1, T2>
  class B<T1, T2> : A<T1,T2>
  {
    // Constructor, must call the base class constructor
    public B(T1 a1, T2 a2) : base(a1, a2)
    { }

    // Access methods - override the access methods of the base class,
    // the override keyword allows you to use the polymorphism mechanism.
    public override T1 GetA1()
    {
      // Invoke the base class method
      return base.GetA1();
    }

    public override T2 GetA2()
    {
      // Invoke the base class method
      return base.GetA2();
    }

    public override void SetA1(T1 a1)
    {
      // Invoke the base class method
      base.SetA1(a1);
    }

    public override void SetA2(T2 a2)
    {
      // Invoke the base class method
      base.SetA2(a2);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 1. Create the instance of base class
      A<double, int> obj1 = new A<double, int>(2.55, 13);

      // Print data of obj1
      Console.WriteLine("obj1.a1 = " + obj1.GetA1());
      Console.WriteLine("obj1.a2 = " + obj1.GetA2());

      // 2. Create the instance of derived class
      B<float, char> obj2 = new B<float, char>(3.8f, 'X');

      // Print data of obj2
      Console.WriteLine("obj2.a1 = " + obj2.GetA1());
      Console.WriteLine("obj2.a2 = " + obj2.GetA2());

      // Change data to obj2
      obj2.SetA1(100.05f);
      obj2.SetA2('$');
      Console.WriteLine("The data in the object has been changed.");
      Console.WriteLine("obj2.a1 = " + obj2.GetA1());
      Console.WriteLine("obj2.a2 = " + obj2.GetA2());

      Console.ReadKey();
    }
  }
}

The result of the program

obj1.a1 = 2.55
obj1.a2 = 13
obj2.a1 = 3.8
obj2.a2 = X
The data in the object has been changed.
obj2.a1 = 100.05
obj2.a2 = $

 


Related topics