Problem overview
A company pays every employee a salary. A manager is an employee too, but earns a bonus on top. The task is two classes: Employee stores a name and a salary, and Manager inherits from it and changes only how pay is worked out.
Employee(name, salary)keeps both values private.Pay()returns the salary, andDescribe()returns text likeAna earns 3000.Manager(name, salary, bonus)passes the name and the salary to the parent and stores the bonus itself. ItsPay()returns the parent's pay plus the bonus.Describe()is not written again for managers.
Ana, an employee with salary 3000, earns 3000. Bob, a manager with salary 4000 and bonus 1000, earns 5000.
Approach
Inheritance lets Manager reuse everything in Employee. A manager is an employee that has a bonus too, so Manager declares only what is new: the bonus field and a different Pay.
The interesting part is Describe. It is written once, in Employee, and builds its text with a call to Pay(). When it runs on a manager, that call reaches the manager's version of Pay, so the bonus shows up without a second Describe. This works because Pay is declared virtual in the parent and override in the child: a call to a virtual method runs the version belonging to the object's real type.
The salary is private to Employee, so Manager can't read it. That is why the manager's pay is written as the parent's Pay() plus the bonus: the salary stays with the code that owns it.
Step by step
- Employee's fields: a
private string nameand aprivate int salary. Not evenManagercan use them. - Employee's constructor stores both parameters with
this.nameandthis.salary. Payreturnssalary. The keywordvirtualin its header allows a subclass to replace it.Describereturnsname, then" earns ", thenPay(), joined with+. Adding anintto a string with+turns the number into text. CallingPay()rather than readingsalaryis what makes managers work.- Manager: declare it as
class Manager : Employee, with its ownprivate int bonus. - Manager's constructor has
: base(name, salary)in its header, which runs Employee's constructor first. Its body storesthis.bonus. - Manager's
Payis markedoverrideand returnsbase.Pay() + bonus.
The solution in C#
class Employee {
private string name;
private int salary;
public Employee(string name, int salary) {
this.name = name;
this.salary = salary;
}
public virtual int Pay() {
return salary;
}
public string Describe() {
return name + " earns " + Pay();
}
}
class Manager : Employee {
private int bonus;
public Manager(string name, int salary, int bonus) : base(name, salary) {
this.bonus = bonus;
}
public override int Pay() {
return base.Pay() + bonus;
}
}
Using it:
Employee[] all = {
new Employee("Ana", 3000),
new Manager("Bob", 4000, 1000),
new Manager("Cleo", 5000, 0)
};
foreach (var e in all) {
Console.WriteLine(e.Describe());
}
This prints:
Ana earns 3000
Bob earns 5000
Cleo earns 5000
The array holds all three people as Employee, yet Bob's line includes his bonus: the call to Pay() inside Describe picks the version for the real type.
Details worth knowing
basehas two jobs. In the constructor header,: base(name, salary)calls the parent's constructor. Inside a method,base.Pay()calls the parent's version of that method.- Overriding needs both keywords. The parent's method must be
virtual(or abstract) and the child's must beoverride. Withoutvirtualin the parent, the child'soverridedoes not compile. - Private is not inherited access. A
Managerobject contains a salary, but onlyEmployee's code can read it. - Constructors run parent first. The parent's constructor fills the name and the salary, then the manager's body stores the bonus.
Edge cases and common mistakes
- Calling
Pay()instead ofbase.Pay()inManager.Pay. The method then calls itself forever. The program crashes with a stack overflow instead of returning a number. - Making the salary public to use it in
Manager. This works but gives up the protection the lesson is about. Use the parent'sPay(). - Writing
Describeagain inManager. It is unnecessary: the inherited one already shows the manager's pay. - Bonus 0. A manager with bonus 0 earns exactly the salary: Cleo above earns 5000, as an employee with the same salary would.
- Forgetting
: base(name, salary).Employeehas no constructor without parameters, so the manager's constructor wouldn't compile.
Testing
employee Ana 3000givesAna earns 3000.manager Bob 4000 1000givesBob earns 5000.manager Cleo 5000 0givesCleo earns 5000.
What the compiler tells you
Two of the common slips are caught at compile time, with messages that name the problem:
- If the parent's method isn't marked
virtual, anoverridein the child fails witherror CS0506: 'M.Pay()': cannot override inherited member 'E.Pay()' because it is not marked virtual, abstract, or override(with your own class names). - If the child's constructor doesn't pass the parent's arguments with
: base(...), and the parent has no parameterless constructor, you geterror CS7036: There is no argument given that corresponds to the required parameter 'n' of 'E.E(string, int)', again with your own names.
The slip that is not caught at compile time is the recursion. Writing Pay() + bonus inside Manager.Pay is valid C#: it just calls the same method. Every call starts another call, none ever returns, and the program ends with a stack overflow.
Why this design is useful
Because Describe depends only on Pay(), adding another kind of employee later needs only a new Pay. A contractor class or a part-time class could override Pay and automatically get the right description. The text format lives in one place, and the pay rule of each kind lives in its own class.
Key takeaways
- A subclass declares only what is new and inherits the rest.
virtualplusoverridemakes a call run the version for the object's real type.base(...)andbase.Method()reach the parent's constructor and methods.
