Inheritance
Problem
Two copies of the same code
The payroll program has an Employee class with a name, a salary and a method for a raise. Then you needed a manager too, who also gets a bonus. The fastest way was to copy Employee into a new class Manager and add the bonus.
A month later someone enters a raise of -10% instead of 10%. So you add a check to Employee. You forget it in Manager:
// in Employeepublic void Raise(int percent){ if (percent > 0) { Salary += Salary * percent / 100; }}
// in Manager: a copy from before the checkpublic void Raise(int percent){ Salary += Salary * percent / 100;}Employee ivan = new Employee("Ivan", 2000);Manager maria = new Manager("Maria", 3000, 500);ivan.Raise(-10);maria.Raise(-10);Console.WriteLine(ivan);Console.WriteLine(maria);Ivan: €2000Maria: €2700Ivan's salary is protected, but Maria's dropped by €300. The two classes are 90% the same, yet every fix has to be made twice.
The idea
The shared part goes in the parent
A manager is an employee, just with something extra. Say it in code: class Manager : Employee. In Manager you write only what is new, the bonus:
class Manager : Employee{ public int Bonus { get; private set; }
public Manager(string name, int salary, int bonus) : base(name, salary) { Bonus = bonus; }}
Manager maria = new Manager("Maria", 3000, 500);maria.Raise(-10);maria.Raise(10);Console.WriteLine(maria);Console.WriteLine("Bonus: €" + maria.Bonus);Maria: €3300Bonus: €500Manager now has everything Employee has: a name, a salary, Raise() with the check, ToString(). That is why the -10% raise did not go through. The check is written once and works for both classes.
This is called inheritance. Employee is the parent (base class), and Manager is the child. The child takes everything from the parent and adds its own.
To check whether inheritance makes sense, use the sentence "X is a Y". "A manager is an employee" is true. "An invoice is an employee" is not, so the invoice does not inherit from Employee, even if it has an amount and a name.
Try it
What does the intern get?
Intern has nothing of its own except a constructor:What will the program print?
class Intern : Employee{ public Intern(string name, int salary) : base(name, salary) { }}
Intern ana = new Intern("Ana", 1200);ana.Raise(5);Console.WriteLine(ana);Raise() and ToString() are inherited from Employee. 5% of 1200 is 60, and 1200 + 60 = 1260.
The idea
The parent comes first
name and salary are private in Employee. The child has them but cannot see them: to the child they are hidden, as they are to any other class. So how does Manager give them a value?
By calling the parent constructor:
public Manager(string name, int salary, int bonus) : base(name, salary){ Bonus = bonus;}: base(name, salary) calls the Employee constructor with these arguments. It sets up the name and the salary, and then the body of the Manager constructor sets up the bonus. You write base after the constructor's parentheses, before the body.
There is one object. It holds both the parent's part and the child's part, and each part is set up by its own constructor.
public Manager(string name, int salary, int bonus) : base(name, salary){ Bonus = bonus;}
Manager maria = new Manager("Maria", 3000, 500);Console.WriteLine(maria + ", bonus €" + maria.Bonus);Step by step, in words
- new sets aside one object. It holds the Employee part and the Manager part.
- The Manager constructor starts with the three arguments.
- The parent comes first: base(name, salary) calls the Employee constructor, before the body.
- The Employee constructor sets up its part: name and salary.
- Only then does the body of Manager set up the bonus.
- There is one object, and each part is set up by its own constructor.
More precisely, if you are curious
- Constructors are not inherited.
Managerhas no constructorManager(string name, int salary)unless you write one. - If the parent has only a constructor with parameters and the child passes nothing, the program does not compile. The compiler does not know what name and what salary to give.
Try it
Which constructor runs first?
Here the child does not call the parent explicitly:
What will the program print?
class Account{ public Account() { Console.WriteLine("Account opened"); }}
class SavingsAccount : Account{ public SavingsAccount() { Console.WriteLine("Interest: 3%"); }}
SavingsAccount savings = new SavingsAccount();: base(...), the compiler adds a call to the parent constructor with no parameters. The parent always comes first. That is why "Account opened" is printed first.Try it
Find the bug
Maria should have a salary of €3000 and a bonus of €500. But the program prints:
Maria: €500Which line is the bug on?
Pick the line with the bug.
The parent gets the bonus, not the salary. Arguments are matched to parameters by position, so Employee gets 500 as the salary. The fix is : base(name, salary).
The idea
protected: only for the children
At the end of the year, the manager's bonus is added to the salary. The method is in Manager, but the salary is private in Employee. A getter is not enough: you have to change it.
There is a third access level between private and public: protected. The class and its children can see such a member, and everyone else cannot:
class Employee{ public string Name { get; private set; } public int Salary { get; protected set; } // ... the rest is as before}
class Manager : Employee{ // ... Bonus and the constructor
public void MergeBonus() { Salary += Bonus; Bonus = 0; }}Manager maria = new Manager("Maria", 3000, 500);maria.MergeBonus();Console.WriteLine(maria);Console.WriteLine("Bonus: €" + maria.Bonus);Maria: €3500Bonus: €0In C# you more often open up not the whole property but only the setter: { get; protected set; }. Anyone can read the salary, but only Employee and its children can change it.
protected weakens the protection: any child can change the salary without going through the check in Raise(). That is why fields stay private by default. protected is for the cases where the child really has to touch the field.
More precisely, if you are curious
- In C#,
protectedis only for the class and its children. There is alsoprotected internal: the children plus everything in the same project.
Try it
What is missing?
An intern is an employee. What goes in the blank?
class Intern Employee
{
public Intern(string name, int salary) : base(name, salary)
{
}
}class Intern : Employee. The same colon also goes before base(...).The idea
One parent and a common ancestor
A class has exactly one parent. It cannot inherit from both Employee and Student. That is why inheritance is single.
The chain can be long. Intern inherits from Employee. And what does Employee inherit from, since it has no :? Every class without a parent inherits from object. This is the common parent of all classes. That is why even an empty class has methods:
class Ticket{}
Ticket a = new Ticket();Ticket b = new Ticket();Console.WriteLine(a.Equals(a));Console.WriteLine(a.Equals(b));Console.WriteLine(a.ToString());TrueFalseTicketEquals() comes from object. For now it only tells you whether two variables point to the same object.
ToString() comes from there too. If you do not write it, it returns the class name, which is why "Ticket" is printed above. In the "Classes and objects" module you wrote your own ToString() with override. Now you know that this replaces the version from object. What exactly override means is in the next lesson.
More precisely, if you are curious
- A class with
sealedbeforeclasscannot be inherited from.stringis one. objectis a short name forSystem.Object. A variable of typeobjectcan point to any object at all:object anything = maria;.
Exercise
Child classes
Solve the tasks in your editor. The parent classes are from the "Classes and objects" module. If you do not have them at hand, write them again: they are short.
1. Family car. SimpleCar has a speed and a method Accelerate() that raises it by 10. Write FamilyCar, which inherits from SimpleCar, starts with 4 seats and has a method AddSeat(). Create a family car, speed it up and add a seat:
Speed: 10 km/hSeats: 52. Student. Person has a name, an age and Introduce(). Write Student, which inherits from Person and adds an average grade Grade (a decimal number). Use the parent constructor.
I am Elena, 19 years old.Grade: 5.53. Breakdance. DanceMove has a name, a duration in seconds and Perform(). Write BreakDanceMove with a complexity from 1 to 10 and a method ShowComplexity().
Performing moonwalk for 5 secondsPerforming windmill for 12 secondswindmill: complexity 8 out of 104. Electric car. Car has a brand, fuel in liters and the kilometers driven. Refuel(liters) adds fuel, and Drive(distance) uses 1 liter per 10 km and adds the kilometers (if there is not enough fuel, it says so and does not go). Write ElectricCar:
- with no fuel, and with a battery that starts at 100%;
- with an efficiency: how many km it drives on 1% of battery (5 for the "Tesla");
Charge()charges the battery to 100%;DriveElectric(distance)uses battery instead of fuel and also adds the kilometers.
private in Car. How does the child change them? Hint: the card on protected.Skoda: 150 km, fuel 35 LTesla: 200 km, battery 60%Not enough battery for 400 kmTesla: 600 km, battery 20%More tasks
- Worker.
Workerinherits fromPersonand adds a job title and a methodWork():
I am Peter, 34 years old.My job: accountant- Intern with a mentor. Add a field with the mentor's name to
Internand pass it in the constructor, together with the name and the salary for the parent.
Test