Dziedziczenie jest jednym z filarów programowania obiektowego. Dzięki temu mechanizmowi możemy korzystać z elementów innej klasy. Elementy, które posiadają prywatny modyfikator dostępu nie są dziedziczone. Używając dziedziczenia możemy skrócić znacząco kod. Należy jednak pamiętać o tym, że stosując język C# nie możemy dziedziczyć po więcej niż jednej klasie.
Dla zobrazowania mechanizmu dziedziczenia napiszmy najpierw dwie kasy pracownik. W przykładzie użyję modyfikatora dostępu public dla ułatwienia zrozumienia bieżącego tematu. O modyfikatorach dostępu oraz enkapsulacji napiszę w innym wpisie. Zobaczmy kod:

public class PracownikSystemCzasowy {
        public string Imie;
        public string Nazwisko;
        public string Email;
        public string Adres;
        public float GodzinyPensja;

        public void WyswietlPodstawoweDane() {
            Console.WriteLine("Imię pracownika: " + Imie);
            Console.WriteLine("Nazwisko pracownika: " + Nazwisko);
            Console.WriteLine("Email pracownika: " + Email);
            Console.WriteLine("Adres pracownika: " + Adres);
        }
    }

    public class PracownikSystemAkordowy {
        public string Imie;
        public string Nazwisko;
        public string Email;
        public string Adres;
        public float AkordPensja;

public void WyswietlPodstawoweDane() {
            Console.WriteLine("Imię pracownika: " + Imie);
            Console.WriteLine("Nazwisko pracownika: " + Nazwisko);
            Console.WriteLine("Email pracownika: " + Email);
            Console.WriteLine("Adres pracownika: " + Adres);
        }    }

W tych przykładowych klasach jest kilka zmiennych, które przechowują te same informacje, a więc możemy je „wyciągnąć” do klasy bazowej. Taką samą czynność możemy zastosować dla metody WyswietlPodstawoweDane(), która występuje w obu klasach i jest identyczna. Zobaczmy kod:

public class Pracownik {
        public string Imie;
        public string Nazwisko;
        public string Email;
        public string Adres;

        public void WyswietlPodstawoweDane() {
            Console.WriteLine("Imię pracownika: " + Imie);
            Console.WriteLine("Nazwisko pracownika: " + Nazwisko);
            Console.WriteLine("Email pracownika: " + Email);
            Console.WriteLine("Adres pracownika: " + Adres);
        }    }

    public class PracownikSystemCzasowy:Pracownik {
        public float GodzinyPensja;
        
    }

    public class PracownikSystemAkordowy:Pracownik {      
        public float AkordPensja;
        
    }

Poprzez zastosowanie dziedziczenia skróciliśmy kod. Teraz klasy pochodne mają tylko specyficzne dla nich zmienne. Zobaczmy przykładowe zastosowanie tych klas:
Pełen kod:

using System;
    public class Pracownik {
        public string Imie;
        public string Nazwisko;
        public string Email;
        public string Adres;

        public void WyswietlPodstawoweDane() {
            Console.WriteLine("Imię pracownika: " + Imie);
            Console.WriteLine("Nazwisko pracownika: " + Nazwisko);
            Console.WriteLine("Email pracownika: " + Email);
            Console.WriteLine("Adres pracownika: " + Adres);
        }
    }

    public class PracownikSystemCzasowy:Pracownik {
        public float GodzinyPensja;
        public PracownikSystemCzasowy() {
        }
    }

    public class PracownikSystemAkordowy:Pracownik {      
        public float AkordPensja;
        public PracownikSystemAkordowy() {
        }
    }
class Program {

    static void Main() {
        PracownikSystemAkordowy akordowy = new PracownikSystemAkordowy();
        PracownikSystemCzasowy czasowy = new PracownikSystemCzasowy();
        Console.WriteLine("Pracownik system akordowy:");
        akordowy.Imie = "Adam";
        akordowy.Nazwisko = "Adamski";
        akordowy.Adres = "Kraków";
        akordowy.Email = "brak";
        akordowy.WyswietlPodstawoweDane();
        Console.WriteLine("Pracownik system czasowy:");
        czasowy.Imie = "Krystian";
        czasowy.Nazwisko = "Kowalski";
        czasowy.Adres = "Warszawa";
        czasowy.Email = "brak";
        czasowy.WyswietlPodstawoweDane();
        Console.ReadLine();
    }


}

Rezultat działania aplikacji:

dziedziczenie1

Rys. Wyświetlenie dwóch pracowników

Bez problemu możemy się odnieść z poziomu obiektu do zmiennej z klasy bazowej, oraz do funkcji WyświetlDanePodstawowe(). Oczywiście mamy również dostęp do specyficznych zmiennych z klas pochodnych zobaczmy:

dziedziczenie2

Rys. Dostęp do zmiennej o nazwie AkordPensja klasy PracownikSystemAkordowy

dziedziczenie3

Rys. Dostęp do zmiennej o nazwie GodzinyPensja klasy PracownikSystemCzasowy

Klasa pochodna nie może mieć więcej niż jedną klasę bazową zobaczmy przykład:

public class PracownikSystemCzasowy:Pracownik,PracownikSystemAkordowy {
        public float GodzinyPensja;
        
    }

Kompilator zwróci nam następujący błąd:

dziedziczenie4

Rys. Błąd zgłoszony przez kompilator

Możliwe jest jednak wielopoziomowe dziedziczenie czyli możemy dziedziczyć po klasie pochodnej zobaczmy przykład:

using System;
    public class Pracownik {
        public string Imie;
        public string Nazwisko;
        public string Email;
        public string Adres;
        public void WyswietlPodstawoweDane() {
            Console.WriteLine("Imię pracownika: " + Imie);
            Console.WriteLine("Nazwisko pracownika: " + Nazwisko);
            Console.WriteLine("Email pracownika: " + Email);
            Console.WriteLine("Adres pracownika: " + Adres);
        }
    }

    public class PracownikSystemCzasowy : Pracownik {
        public float GodzinyPensja;
        public void WyswietlGodzinyPensja() {
            Console.WriteLine("Pensja za godziny pracy wynosi: "+ GodzinyPensja);
        }
    }
    public class PracownikSystemTest : PracownikSystemCzasowy {
        public string TestowaZmienna;
        public void WyswietlDotatkoweDane() {
            Console.WriteLine("Testowa zmienna:  " + TestowaZmienna);
        }
    }

    public class PracownikSystemAkordowy : Pracownik {
        public float AkordPensja;
       
    }
class Program {

    static void Main() {
        PracownikSystemTest test = new PracownikSystemTest();
        Console.WriteLine("Pracownik test:");
        test.Imie = "Kamiil";
        test.Nazwisko = "K.";
        test.Adres = "Warszawa";
        test.Email = "brak";
        test.WyswietlPodstawoweDane();
        test.GodzinyPensja = 10;
        test.WyswietlGodzinyPensja();
        test.TestowaZmienna = "Dodatkowa zmienna testowa";
        test.WyswietlDotatkoweDane();
        Console.ReadLine();
    }
}

Rezultat działania aplikacji:

dziedziczenie5

Rys. Wyświetlenie obiektu klasy PracownikSystemTest

Obiekt klasy PracownikSystemTest ma dostęp do elementów klasy Pracownik oraz PracownikSystemCzasowy. Dodatkowo możemy się odwołać do specyficznych elementów klasy PracownikSystemTest.

Konstruktory w mechanizmie dziedziczenia

Tworząc obiekt klasy pochodnej wywoływany jest oprócz jej konstruktora, konstruktor klasy bazowej zobaczmy przykład:

using System;
public class Pracownik {
    public string Imie;
    public string Nazwisko;
    public string Email;
    public string Adres;
    public Pracownik() {
        Console.WriteLine("Wywołany jest konstruktor klasy Pracownik");
    }
    public void WyswietlPodstawoweDane() {
        Console.WriteLine("Imię pracownika: " + Imie);
        Console.WriteLine("Nazwisko pracownika: " + Nazwisko);
        Console.WriteLine("Email pracownika: " + Email);
        Console.WriteLine("Adres pracownika: " + Adres);
    }
}

public class PracownikSystemCzasowy : Pracownik {
    public float GodzinyPensja;
    public PracownikSystemCzasowy() {
        Console.WriteLine("Wywołany jest konstruktor klasy PracownikSystemCzasowy");
    }
    public void WyswietlGodzinyPensja() {
        Console.WriteLine("Pensja za godziny pracy wynosi: " + GodzinyPensja);
    }
}
public class PracownikSystemTest : PracownikSystemCzasowy {
    public string TestowaZmienna;
    public PracownikSystemTest() {
        Console.WriteLine("Wywołany jest konstruktor klasy PracownikSystemTest");
    }
    public void WyswietlDotatkoweDane() {
        Console.WriteLine("Testowa zmienna:  " + TestowaZmienna);
    }
}

class Program {
    static void Main() {
        PracownikSystemCzasowy pracownik = new PracownikSystemCzasowy();
        Console.ReadLine();
    }
}

Rezultat działania aplikacji:

dziedziczenie6

Rys. Wywołanie konstruktorów podczas mechanizmu dziedziczenia

Najpierw został wywołany konstruktor klasy bazowej a następnie klasy pochodnej. W przypadku dziedziczenia wielopoziomowego będą wywoływane konstuktory według hierarchii zobaczmy przykład:
W kodzie aplikacji z poprzedniego przykładu zmieńmy tylko metodę Main():

class Program {
    static void Main() {
        PracownikSystemTest pracownik = new PracownikSystemTest();
        Console.ReadLine();
    }
}

Rezultat działania aplikacji:

dziedziczenie7

Rys. Wywołanie konstruktorów podczas mechanizmu dziedziczenia

Podczas tworzenia obiektu klasy PracownikSystemTest wywoływany jest najpierw konstruktor, który jest najwyższy w hierarchii, następnie wywoływane są konstruktory, które są niżej , a na samym końcu wywołany jest konstruktor klasy, której tworzony jest obiekt.

Mamy możliwość wywołania konstruktora z parametrami klasy bazowej używając słowa kluczowego base. Możemy wtedy wybrać, z którego konstuktrora klasy bazowej chcemy skorzystać, zobaczmy przykład:

dziedziczenie8

Rys. Visual Strudio podpowiada jakie konstruktory mamy dostępne.

Zmodyfikujmy kod naszego przykładu aby lepiej zobrazować możliwości wykorzystania słowa kluczowego base:

using System;
public class Pracownik {
    public string Imie;
    public string Nazwisko;
    public string Email;
    public string Adres;
     public Pracownik() {
        Console.WriteLine("Wywołany jest konstruktor klasy Pracownik");
    }

    public Pracownik(string imie, string nazwisko, string email,string adres) {
        Imie = imie;
        Nazwisko = nazwisko;
        Email = email;
        Adres = adres;

        Console.WriteLine("Wywołany jest konstruktor z parametrami  klasy Pracownik");    
    } 

    public void WyswietlPodstawoweDane() {
        Console.WriteLine("Imię pracownika: " + Imie);
        Console.WriteLine("Nazwisko pracownika: " + Nazwisko);
        Console.WriteLine("Email pracownika: " + Email);
        Console.WriteLine("Adres pracownika: " + Adres);      
    }
}

public class PracownikSystemCzasowy : Pracownik {
    public float GodzinyPensja;
    public PracownikSystemCzasowy(string imie, string nazwisko, string email, string adres, float pensja): base( imie,nazwisko,email,adres) {
        GodzinyPensja = pensja;
        Console.WriteLine("Wywołany jest konstruktor klasy PracownikSystemCzasowy");     
    }
    public void WyswietlGodzinyPensja() {
        Console.WriteLine("Pensja za godziny pracy wynosi: " + GodzinyPensja);
    }
}
 

class Program {
    static void Main() {
        PracownikSystemCzasowy pracownik = new PracownikSystemCzasowy("Aga","Kowalska","AagaKow@pisz-kod.pl","Warszawa",5000);
        pracownik.WyswietlPodstawoweDane();
        pracownik.WyswietlGodzinyPensja();
        Console.ReadLine();
    }
}

Rezultat działania aplikacji:

dziedziczenie9

Rys. Wyświetlenie stanu obiektu

Dzięki zastosowaniu słowa kluczowego base przenieśliśmy część odpowiedzialności przypisania wartości dla pól na konstruktor klasy bazowej. Przypisaliśmy w tym konstruktorze wartości dla pól klasy bazowej. Natomiast w konstruktorze z parametrami w klasie pochodnej przypisaliśmy wartości tylko dla pola, które w niej występuje.

Podsumowanie:
Dziedziczenie jest jednym z filarów programowania obiektowego. Dzięki temu mechanizmowi możemy znacząco zmniejszyć ilość kodu. Należy pamiętać, że podczas tworzenia obiektu klasy pochodnej wywoływany jest najpierw konstruktor klasy bazowej. Używając słowa kluczowego base możemy wywołać konstruktor z parametrami klasy bazowej. Dzięki tej możliwości możemy stworzyć w klasie bazowej konstruktory, z których będą mogły korzystać klasy pochodne w zależności od potrzeb.