Monday 20 July 2015

abstract, static, virtual, new

An abstract class is a class that cannot be instantiated, but can be inherited from; it is the ideal way to define a base class, that is the class that we inherit from. For example we could create an employee class that derives from the base class person.


abstract class Person { }

class Employee : Person { }


In the above we create a base Person class and a subclass Employee, that is Employee inherits from Person, so for example if we add properties for first and last names, as well as birthdate to Person.


abstract class Person {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime Birthdate{ get; set; }
}


Our employee class would inherit these properties from our Person class.


class Employee : Person { }


Meaning that we could very will do something like the following


class Program
    {
        static void Main(string[] args)
        {
            var emp = new Employee
            {
                FirstName = "Pawel",
                LastName = "Ciucias",
                BirthDate = new DateTime(1984, 1, 31)
            };

        }
    }


we see that we can instantiate an employee with all the properties that are defined in it's base class Person.

Let's add two properties and function to our base class


abstract class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
    public string FullName { get { return $"{FirstName} {LastName}"; } }
    public int Age
    {
        get
        {
            var today = DateTime.Today;
            var age = today.Year - BirthDate.Year;
            return BirthDate > today.AddYears(-age) ? --age : age;
        }
    }
    public string Print()
    {
        return $"{FullName} {BirthDate.ToShortDateString()}";
    }
}


now that we have a properties that return our Persons age, full name and a function that prints out a representation of the person class, let's implement our Employee class


class Employee : Person
{
    static int runningId = 0;
    public int Id { get; } = ++runningId;
    public double Wage { get; set; }
    
    public new string Print() =>  $"{base.Print()} EmpId:{Id}";
    public virtual double GetIncome(double hoursWorked) {
        return hoursWorked * Wage;
    }
}


Here we create a static runningId property, when a property, class, field, event or function are declared as static, they have three characteristics
  • Can't be instantiated, it always exists;  you can't instantiated it, it's always there.
  • Shared between all of it's instances, in this case every instance of the employee class will share the same runningId field.
  • Can't be referenced from an instance of their containing class, if runningId was public it would have to be referenced via Employee.runningId;

public int Id { get; } = ++runningId;


next we created a read-only property Id, which is initialized to runningId and increments runningId by 1. the ++ syntax is the same as runningId = runningId + 1; it's just short hand, however it matters whether you use ++runningId or runningId++ in both cases runningId will be incremented by 1 the difference is that if the ++ precedes the field or property it's first incremented then assigned whereas if it follows the field or property it's assigned then incriminated.


public new string Print() =>  $"{base.Print()} EmpId:{Id}";


We created another Print function in the Employee call in which we call the base implementation and append it with the employee number, notice the new keyword we use; this new keyword let's the compiler know that we on purpose have "improved" the base implementation of print. the nice thing is however that should we ever cast our Employee to a Person and call the Print method we'll call the Person version of it.


static void Main(string[] args)
{
    var emp = new Employee { FirstName = "Pawel", LastName = "Ciucias" ,
                             BirthDate = new DateTime(1984,1,31) };

    Console.WriteLine(emp.Print());
    Console.WriteLine(((Person)emp).Print());
}


In the above code we'll first see the print method from the Employee class and then from the Person class.


public double Wage { get; set; }
public virtual string GetIncome(double hoursWorked) {
    return (hoursWorked * Wage).ToString("C");
}


Finally we declared a a double to store our employees wage and a virtual GetIncome function that takes in hours worked and returns the employees income for those hours.

The virtual keyword basically means that a subclass can but doesn't have to override our function, method, property, indexer or event declaration. We'll take a look at what this means in our implementation of our Manager class.


class Manager : Employee {
    public override string GetIncome(double hoursWorked)
    {
        return Wage.ToString("C");
    }
}


Our Manager class inherits from the Employee class and overrides the GetIncome function, the difference between override and new in this context is that when you override a function firstly it has to be declared as virtual in the base class and secondly it replaces the base implementation. That is if you cast a Manger as an Employee and call the GetIncome function you'd still get the Manager's implementation of it.


class Program
{
    static void Main(string[] args)
    {
        var emp = new Employee
        {
            FirstName = "Pawel",
            LastName = "Ciucias",
            BirthDate = new DateTime(1984, 1, 31),
            Wage = 31.50
        };

        Console.WriteLine(emp.Print());
        Console.WriteLine(((Person)emp).Print());
        Console.WriteLine(emp.GetIncome(40));

        var man = new Manager
        {
            FirstName = "Tomek",
            LastName = "Ciucias",
            BirthDate = new DateTime(1988, 8, 28),
            Wage = 120000
        };

        Console.WriteLine(man.GetIncome(50));
        Console.WriteLine(((Employee)man).GetIncome(50));
    }
}


This doesn't cover all possible combinations of the keywords that we've covered, but hopefully increases your understanding of what they do and when to use them.

by combination here's what I mean

class Employee : Person
{
    ...
    public virtual new string Print() => $"{base.Print()} EmpId:{Id}";
    ...
}


In our employee class we could have marked the Print function as virtual and new, meaning that our employee class will use it's implementation as before, and as before if we cast our employee to a Person it'll use Persons implementation.


class Manager : Employee
{
    ...
    public override string Print() => $"{FullName} EmpId:{Id} Salery:${Wage}";
    ...
}


However if we override the Print function in the Manager class, and downcast the manager to an Employee we'll still use the Manager's version of the print function. To make things more confusing though if we cast our Manager to a Person, well then we'll use the Person class implementation of Print because in Person the print function is not virtual.

here's a summary

abstract: can't be instantiated, but can be inherited; an abstract class must be a base class, a public abstract property or method or function can't contain a body or value and must be implemented in a subclass.
subclass: a class that inherits from a base class
superclass: the opposite of a sublcass, the class that is inherited from
static: only one instance that can't be instantiated it just exists for the duration of the application and is shared by everything.
new: in the context of inheritance when a property or function as is marked as new, you're letting the compiler now that on purpose you're improving a property or function in a subclass, if you cast that class back to it's base you'll be able to leverage that base class's implementation of the property or function.
virtual: allows you but doesn't commit you to "improve" via the new keyword or "replace" via the override keyword in a subclass.
override: can only be done on properties or methods that have been marked as virtual in a base class, overriding completely replaces the implementation of a virtual property or function, meaning that even if you cast to a base class you'll still only be able to access the subclass's version.