W dzisiejszym poście chciałbym poruszyć temat dziedziczenia w języku C#.
Czym jest dziedziczenie? W jaki sposób poprawnie używać dziedziczenia?
Klasy bazowa i pochodna, odwołanie do klasy bazowej, wielokrotne dziedziczenie.
Zaczynamy!
Czym jest dziedziczenie?
Dziedziczenie to kluczowy mechanizm obiektowości. Dziedziczenie pozwala na powielanie funkcjonalności wobec różnych klas w ten sposób nie musimy pisać ciągle samego kodu.
Dziedziczenie jest również jednym z najważniejszych pojęć w programowaniu obiektowym. Dziedziczenie pozwala na powielanie funkcjonalności wobec różnych klas, przez co nie musimy pisać ciągle samego kodu, co daje możliwość ponowego wykorzystania kodu, co pozwoli nam na zaoszczędzenie czasu podczas jego implementacji.
Podczas tworzenia nowej klasy, zamiast pisać zupełnie od nowa wszystkie składowe tej klasy, Ty jako programista możesz postanowić, że nowa klasa ma dziedziczyć z istniejącej już klasy. Istniejąca klasa nazywana jest klasą bazową a nowa klasa dziedzicząca po klasie bazowej nosi nazwę klasy pochodnej.
Idea dziedziczenia realizuje związek to-jest (IS-A). Wiele doświadczonych programistów (w czytanych przeze mnie blogach, czy artykuałach o programowaniu) przytacza przykład psa jako zobrazowanie definicji dziedziczenia. Więc i ja przytoczę to porównanie: ssak ‘to-jest’ zwierzę, pies ‘to-jest’ ssak ale pies ‘to-jest’ też zwierzę, i tak dalej.
Klasa bazowa i pochodna
Klasa może dziedziczyć z jednej klasy, ale może implementować wiele interfejsów.
Składnia dziedziczenia:
1 2 3 4 5 6 7 8 |
modyfikator_dostepu class klasa_bazowa { … } class klasa_pochodna : klasa_bazowa { … } |
Przejdźmy przez poniższy przykład, który w czytelniejszy sposób pomoże nam zrozumieć idee dziedziczenia:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
using System; namespace Heirdom { class Program { static void Main(string[] args) { Rectangle r = new Rectangle(); r.SetWidth(4); r.SetHeight(5); // Calculate surface Console.WriteLine("Surface rectangle: {0}", r.CalculateSurface()); Console.ReadKey(); // The result of the program // Surface rectangle: 20 } } } //base class class Form { // access modifier protected // fields are available for the class and classes that they inherit from it protected int width; protected int height; public void SetHeight(int w) { height = w; } public void SetWidth(int s) { width = s; } } class Rectangle:Form { public int CalculateSurface() { //we have access to fields from the base class return height * width; } } |
Odwołanie do klasy bazowej
Klasa pochodna dziedziczy składowe klasy bazowej, tj. pola, metody. Podczas dziedziczenia wielokrotnie pojawi się potrzeba uzyskania dostępu do składowych klasy bazowej. Dostęp do takich pól czy metod jest możliwy po użyciu słowa kluczowego base. Może ono zostać również użyte do przekazania parametrów konstruktora do klasy bazowej. Poniżej przykład, który pozwoli lepiej zrozumieć regułu użycia słowa kluczowego base:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
using System; namespace BaseInheritance { class Program { static void Main(string[] args) { Deck d = new Deck(4, 5); d.DisplayInformation(); Console.ReadKey(); } } } using System; namespace BaseInheritance { class Rectangle { // access modifier protected // fields are available for the class and classes that they inherit from it protected int width; protected int height; public Rectangle (int w, int s) { width = w; height = s; } public int CalculateSurface() { return width * height; } public void DisplayInformation() { Console.WriteLine("Height: {0}", height); Console.WriteLine("Width: {0}", width); Console.WriteLine("Surface: {0}", CalculateSurface()); } } } using System; namespace BaseInheritance { class Deck:Rectangle { public Deck(int w, int s) : base(w, s) { } public int Cost() { int cost; cost = CalculateSurface() * 50; return cost; } public void DisplayInformation() { // the key word base allows us to refer to the components of the base class // for the compiler more important variables from the class in which we are // using the base keyword we indicate unambiguously to which component // we want to appeal. Thanks to the following call in the current method we will call // also the method from the base class - more information will be displayed base.DisplayInformation(); Console.WriteLine("Cost: {0}", Cost()); } } } |
Wielokrotne dziedziczenie
W programowaniu obiektowym często się zdarza że musimy używać zupełnie odrębnych klas w ten sam sposób. Aby to było możliwe musimy w jakiś sposób zadeklarować że grupa klas oferuje podobną funkcjonalność. Tytuł punktu jest nieco przewrotny, bo akurat właśnie wielokrotne dziedziczenie (jawne) język C# niestety nie obsługuje wielokrotnego dziedziczenia. Tu należy pamiętać, że klasa może dziedziczyć po jednej klasie bazowej, ale może implementować wiele interfejsów (Interfejsom poświece osobny post).
Do przedstawienia na jakiej zasadzie działa użyje dobrze znanego nam prostokątu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
using System; namespace InheranceInterfaces { class Program { static void Main(string[] args) { Rectangle r = new Rectangle(4, 5); Console.WriteLine(r.DisplayLength(r.lenght)); Console.WriteLine(pr.DisplayWidth(r.width)); Console.WriteLine("Cena to: {0}", r.CalkulateCost(25)); Console.ReadKey(); } } // base class class Form { public int lenght; public int width; public Form(int l, int w) { lenght = l; width = w; } public int CalkulateSurface() { return lenght * width; } } // interface definition public interface CalkulateCost { int CalkulateCost(int surface); } public interface DisplayInformation { string DisplayLenght(int lenght); string DisplayWidth(int width); } class Rectangle : Form, CalkulateCost, DisplayInformation { public Rectangle(int l, int w) : base(l, w) { } // implementation interface method CalkulateCost public int CalkulateCost(int p) { int cost; cost = p * CalkulateSurface(); return cost; } // implementation interface method DisplayLenght public string DisplayLenght(int lenght) { string info = String.Format("Lenght is: {0}", lenght); return info; } // implementation interface method DisplayWidth public string DisplayWidth(int width) { string info = String.Format("Szerokość to: {0}", width); return info; } } } |
Pytania? sugestie? piszcie komentujcie.
Oczywiście cały Przykłady dostępne na GitHubie.