C# Basics - Virtual
2023-02-07 22:01:14  C#  >> Basics

Concept of Virtual Methods

In general, functions are statically compiled into the execution file during compilation, and their relative addresses do not change during the running of the program, that is, they are written to be fixed!

The virtual function is not statically compiled during compilation, and its relative address is uncertain. It will dynamically determine the function to be called according to the object instance at runtime.

The class defined during the declaration is called an declaration class, and the execution A class that is instantiated is called an instance class.

1
2
// AAA is the declaration class, and BBB is the instance class
AAA aaa = new BBB();

PRINCIPLES

To determine which class to be called.

  1. When calling the function of an object aaa, the system will go directly to check the object to affirm it declaration class AAA, that is, to affirm the class to see whether the function called is a virtual function.
    1. If it is not a virtual function, then it will directly execute the function.
    2. If there is virtual keyword, that is, a virtual function, then this time it will not immediately execute the function, but go to check the object’s instance class, which is BBB.
  2. In this instance class, BBB, it will check whether the function is overridden (through the override keyword).
    1. If yes, then OK, it will immediately execute this overridden function in the instance class.
    2. If no, the system will keep looking for the parent class of the instance class and repeat the same check in the instance class until it finds the first parent class that overrides the virtual function, and then executes the overridden function in that parent class.

Purpose of Virtual Methods

  1. Allow derived classes (i.e., their children) to extend the parent class.
  2. Virtual methods are a manifestation of the polymorphic property.
  3. Increase maintainability and clarity in the development process.

Examples of Using Virtual Methods

Before we try to use virtual, let’s review on inheritence between classes.

First, Create a WPF project that has a button function. And create a base class, Person and its derived class, Student.

1
2
3
4
5
6
7
8
9
10
11
12
13
private void Button_Click(object sender, RoutedEventArgs e)
{
}

public class Person
{
public string name = "This is Person.";
}

public class Student : Person
{
public string name = "This is Student.";
}
text

Then, you will see there is a hint on the field, name, in Student class as follows.

text

This means the derived class, Student, has the same field as its base class, Person, called name.
Let’s call them respectively.

1
2
3
4
5
Person person = new Person();  
Student student = new Student();

string v1 = person.name;
string v2 = student.name;
text

Despite having inheritance, they still display what their respective field contains.
You can also add new keyword to the field to make the field belongs to the class. It will return the same result as above.

1
2
3
4
public class Student : Person 
{
new public string name = "This is Student.";
}

Let’s add a test() to make more tests.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void Button_Click(object sender, RoutedEventArgs e)
{
Person person = new Person();
Student student = new Student();

person.test();
student.test();
}

public class Person
{
public string name = "This is Person.";
public string test() => "[test] Person";

}

public class Student : Person
{
public string name = "This is Student.";
public string test() => "[test] Student";
}
text

As name, the test() will return its class’s respective contents.

Now, how about converting Student to Person type.

1
2
3
4
5
Student student = new Student();
Person person = (Person)student;

person.test();
student.test();
text

As shown, even student object is converted into base class type of Person, the result keeps the same.

Remembered the Principles of determining which class to be called above?

  1. When calling the function of an object aaa, the system will go directly to check the object to affirm it declaration class AAA, that is, to affirm the class to see whether the function called is a virtual function.
    1. If it is not a virtual function, then it will directly execute the function.

When calling the objects of student or person, it checked their declaration class: Student and Person

Both of test() in these two class do NOT have virtual keyword, so the function will be directly called and no need to bother their instance class.

Call Derived Functions

Use Virtual in base class and use override in derived class.

1
2
3
4
5
6
7
8
9
10
11
12
13
 public class Person
{
public string name = "This is Person.";
//Use virtual in base class
virtual public string test() => "[test] Person";
}

public class Student : Person
{
public string name = "This is Student.";
//Use override in derived class
override public string test() => "[test] Student";
}
text

See how we called the object as below.

1
2
3
4
5
Student student = new Student();
Person person = (Person)student;

person.test();
student.test();

Let’s follow the Principles step by step to see how the objects were called.

  1. When we called person.test();, it would check its declaration class, Person.

  2. In Person class, the test() has virtual keyword, then go to check the object’s instance class, which is the student‘s instance class: Student.

  3. In Student class, it will check if the function has override keyword. If so, return the overridden method:

    override public string test() => "[test] Student";

Base on Principles, we can easily figure out how the objects are called in an inheritance relationships with the keywords of virtual and override.

Multiple Inheritance

Finally, let’s do one more test on multiple inheritance condition to see how Principles matches.

Add another class called Parent, derived to Student.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Person
{
public string name = "This is Person.";
virtual public string test() => "[test] Person";
}

public class Student : Person
{
public string name = "This is Student.";
override public string test() => "[test] Student";
}

public class Parent : Student
{
public string name = "This is Parent.";
public string test() => "[test] Parent";
}

Called them as before and display them.

1
2
3
4
5
6
7
Student student = new Student();
Parent parent = new Parent();
Person person = (Person)parent;

person.test();
student.test();
parent.test();
text
  1. When called person.test(), it would check its declaration class, Person.
  2. In Person class, the test() has the virtual, so it will check its instance class : Parent.
  3. In Parent class, the test() does not have virtual keyword, so it will follow the Principles:
    1. If no, the system will keep looking for the parent class of the instance class and repeat the same check in the instance class until it finds the first parent class that overrides the virtual function, and then executes the overridden function in that parent class.
  4. It will check Parent‘s inherited class: Student, then found override keyword in Student and return it.

Good Day
😎