1. Τι είναι η Κληρονομικότητα;
Η κληρονομικότητα είναι μια ιδέα στον προγραμματισμό που σου επιτρέπει να δημιουργήσεις μια νέα κλάση με βάση μια υπάρχουσα κλάση. Με την κληρονομικότητα, η νέα κλάση (που ονομάζεται παράγωγη ή υποκλάση) κληρονομεί όλες τις ιδιότητες και τις μεθόδους της υπάρχουσας κλάσης (που ονομάζεται βασική κλάση ή υπερκλάση).
Σκέψου το σαν την καθημερινή ζωή: ένα παιδί μπορεί να κληρονομήσει χαρακτηριστικά από τους γονείς του. Στον προγραμματισμό, μια υποκλάση μπορεί να κληρονομήσει μεθόδους και ιδιότητες από τη βασική κλάση και μπορεί να προσθέσει και τα δικά της μοναδικά χαρακτηριστικά.
Παράδειγμα Κληρονομικότητας
Ας δούμε ένα παράδειγμα για να το καταλάβεις καλύτερα.
Φαντάσου ότι φτιάχνουμε ένα πρόγραμμα που διαχειρίζεται ζώα. Έχουμε μια βασική κλάση Animal
και θέλουμε να φτιάξουμε μια υποκλάση Dog
που κληρονομεί από την κλάση Animal
.
// Βασική κλάση Animal
class Animal
{
public string Name { get; set; }
public void Eat()
{
Console.WriteLine($"{Name} τρώει.");
}
}
// Υποκλάση Dog που κληρονομεί από την Animal
class Dog : Animal
{
public void Bark()
{
Console.WriteLine($”{Name} γαβγίζει!”);
}
}
class Program
{
static void Main()
{
// Δημιουργούμε ένα σκύλο
Dog dog = new Dog();
dog.Name = “Rex”; // Χρησιμοποιούμε την ιδιότητα Name από την κλάση Animal
dog.Eat(); // Καλούμε τη μέθοδο Eat από την κλάση Animal
dog.Bark(); // Καλούμε τη μέθοδο Bark από την υποκλάση Dog
}
}
Αναλυτική Εξήγηση:
- Βασική κλάση
Animal
: Αυτή η κλάση έχει μία ιδιότητα, τοName
, και μία μέθοδοEat()
, η οποία εκτυπώνει ότι το ζώο τρώει. - Υποκλάση
Dog
: Η κλάσηDog
κληρονομεί από τηνAnimal
. Αυτό σημαίνει ότι τοDog
έχει πρόσβαση στην ιδιότηταName
και στη μέθοδοEat()
. Επίσης, προσθέτει τη δική του μέθοδοBark()
, που εκτυπώνει ότι ο σκύλος γαβγίζει. - Δημιουργία ενός αντικειμένου
Dog
: Όταν φτιάχνουμε ένα αντικείμενο της κλάσηςDog
, μπορούμε να χρησιμοποιήσουμε τόσο την ιδιότητα και τη μέθοδο της κλάσηςAnimal
όσο και τη μέθοδο που ορίσαμε στηDog
.
Αποτέλεσμα:
Rex τρώει.
Rex γαβγίζει!
Πλεονεκτήματα της Κληρονομικότητας:
- Επαναχρησιμοποίηση κώδικα: Μπορείς να φτιάξεις νέες κλάσεις που κληρονομούν τον κώδικα από άλλες κλάσεις, χωρίς να χρειάζεται να ξαναγράφεις τον ίδιο κώδικα.
- Προσθήκη νέων λειτουργιών: Μπορείς να προσθέσεις νέες ιδιότητες ή μεθόδους σε μια υποκλάση, χωρίς να αλλάξεις την βασική κλάση.
2. Τι είναι ο Πολυμορφισμός;
Ο πολυμορφισμός είναι μια άλλη βασική ιδέα στον προγραμματισμό, και η λέξη προέρχεται από τα ελληνικά και σημαίνει “πολλές μορφές”. Στον προγραμματισμό, ο πολυμορφισμός σημαίνει ότι ένα αντικείμενο μπορεί να συμπεριφέρεται με διαφορετικούς τρόπους ανάλογα με το πώς το χρησιμοποιείς.
Υπάρχουν δύο κύριοι τύποι πολυμορφισμού:
- Στατικός πολυμορφισμός (static polymorphism): Αυτός επιτυγχάνεται με υπερφόρτωση μεθόδων. Δηλαδή, η ίδια μέθοδος μπορεί να κάνει διαφορετικά πράγματα, ανάλογα με το ποια έκδοση της καλείς.
- Δυναμικός πολυμορφισμός (dynamic polymorphism): Αυτός επιτυγχάνεται με τη χρήση εικονικών μεθόδων (virtual methods) και προκαθορισμένων (override) μεθόδων. Μια μέθοδος μπορεί να αλλάζει τη συμπεριφορά της ανάλογα με την υποκλάση που την υλοποιεί.
Παράδειγμα Πολυμορφισμού
Ας δούμε πώς λειτουργεί ο πολυμορφισμός με τις εικονικές μεθόδους και τις προκαθορισμένες μεθόδους.
// Βασική κλάση Animal με εικονική μέθοδο
class Animal
{
public string Name { get; set; }
// Εικονική μέθοδος που μπορεί να υπερκαλυφθεί
public virtual void Speak()
{
Console.WriteLine($"{Name} κάνει ήχο.");
}
}
// Υποκλάση Dog που υπερκαλύπτει τη μέθοδο Speak
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine($”{Name} γαβγίζει!”);
}
}
// Υποκλάση Cat που υπερκαλύπτει τη μέθοδο Speak
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine($”{Name} νιαουρίζει!”);
}
}
class Program
{
static void Main()
{
// Δημιουργούμε ένα αντικείμενο Dog και ένα Cat
Animal dog = new Dog { Name = “Rex” };
Animal cat = new Cat { Name = “Milo” };
// Καλούμε τη μέθοδο Speak
dog.Speak(); // Καλεί τη μέθοδο από την υποκλάση Dog
cat.Speak(); // Καλεί τη μέθοδο από την υποκλάση Cat
}
}
Αναλυτική Εξήγηση:
- Η βασική κλάση
Animal
:- Έχει μια εικονική μέθοδο
Speak()
, που σημαίνει ότι οι υποκλάσεις μπορούν να υπερκαλύψουν αυτήν τη μέθοδο και να την αλλάξουν.
- Έχει μια εικονική μέθοδο
- Η υποκλάση
Dog
:- Υπερκαλύπτει τη μέθοδο
Speak()
με τη δική της έκδοση που εκτυπώνει ότι ο σκύλος γαβγίζει.
- Υπερκαλύπτει τη μέθοδο
- Η υποκλάση
Cat
:- Υπερκαλύπτει επίσης τη μέθοδο
Speak()
, αλλά με διαφορετική συμπεριφορά, εκτυπώνοντας ότι η γάτα νιαουρίζει.
- Υπερκαλύπτει επίσης τη μέθοδο
- Πολυμορφισμός:
- Παρόλο που το αντικείμενο
dog
είναι τύπουAnimal
, καλεί τη μέθοδοSpeak()
της υποκλάσηςDog
, και το ίδιο γίνεται με τοcat
που καλεί τη μέθοδο της υποκλάσηςCat
. Αυτό είναι ο δυναμικός πολυμορφισμός.
- Παρόλο που το αντικείμενο
Αποτέλεσμα:
Rex γαβγίζει!
Milo νιαουρίζει!
Πλεονεκτήματα του Πολυμορφισμού:
- Ευελιξία: Μπορείς να χρησιμοποιήσεις διαφορετικά αντικείμενα (π.χ.,
Dog
,Cat
) με τον ίδιο τρόπο (π.χ., καλώντας τη μέθοδοSpeak()
), και το κάθε αντικείμενο θα συμπεριφερθεί με τον δικό του τρόπο. - Επεκτασιμότητα: Μπορείς να προσθέσεις νέες κλάσεις με διαφορετικές συμπεριφορές χωρίς να αλλάξεις τον υπάρχοντα κώδικα.
Συμπέρασμα
Κληρονομικότητα:
- Η κληρονομικότητα σου επιτρέπει να επαναχρησιμοποιείς κώδικα από άλλες κλάσεις και να προσθέτεις νέες λειτουργίες σε υποκλάσεις.
- Η βασική κλάση περιέχει κοινές λειτουργίες που μπορούν να χρησιμοποιήσουν οι υποκλάσεις.
Πολυμορφισμός:
- Ο πολυμορφισμός σου επιτρέπει να χρησιμοποιείς αντικείμενα διαφορετικών τύπων με έναν ενιαίο τρόπο, αλλά με διαφορετική συμπεριφορά ανάλογα με τον τύπο του αντικειμένου.
- Η ίδια μέθοδος μπορεί να έχει διαφορετική συμπεριφορά ανάλογα με την υποκλάση που την υλοποιεί.
Και οι δύο αυτές έννοιες είναι πολύ σημαντικές για να φτιάχνεις ευέλικτα και επεκτάσιμα
υπάρχουν μερικά ακόμα σημεία που αξίζει να γνωρίζεις για την κληρονομικότητα και τον πολυμορφισμό στην C#, τα οποία θα σε βοηθήσουν να κατανοήσεις καλύτερα και πιο σε βάθος αυτές τις έννοιες.
1. Προστατευμένες Ιδιότητες και Μέθοδοι (protected)
Όταν χρησιμοποιείς την κληρονομικότητα, μπορείς να έχεις ιδιότητες και μεθόδους που είναι προστατευμένες. Αυτό σημαίνει ότι μπορούν να χρησιμοποιηθούν μόνο από την βασική κλάση και τις υποκλάσεις της, αλλά όχι από εξωτερικό κώδικα (όπως το Main()
).
Παράδειγμα:
class Animal
{
public string Name { get; set; }
// Προστατευμένη μέθοδος που μπορεί να χρησιμοποιηθεί μόνο από υποκλάσεις
protected void Sleep()
{
Console.WriteLine($"{Name} κοιμάται.");
}
}
class Dog : Animal
{
public void Rest()
{
Sleep(); // Μπορούμε να καλέσουμε την προστατευμένη μέθοδο εδώ
}
}
class Program
{
static void Main()
{
Dog dog = new Dog { Name = “Rex” };
dog.Rest(); // Αυτό καλεί την προστατευμένη μέθοδο Sleep από την υποκλάση
}
}
Σημείωση: Η μέθοδος Sleep()
είναι protected και μπορεί να κληθεί μέσα από την υποκλάση Dog
, αλλά όχι απευθείας από τον κώδικα της κλάσης Program
.
2. Το base
στη χρήση της Κληρονομικότητας
Όταν χρησιμοποιείς κληρονομικότητα, μπορείς να καλέσεις μεθόδους και ιδιότητες της βασικής κλάσης μέσα από την υποκλάση χρησιμοποιώντας τη λέξη-κλειδί base
.
Παράδειγμα με base
:
class Animal
{
public string Name { get; set; }
public virtual void Speak()
{
Console.WriteLine($"{Name} κάνει έναν γενικό ήχο.");
}
}
class Dog : Animal
{
public override void Speak()
{
base.Speak(); // Καλεί τη μέθοδο Speak από τη βασική κλάση
Console.WriteLine($”{Name} γαβγίζει!”);
}
}
class Program
{
static void Main()
{
Dog dog = new Dog { Name = “Rex” };
dog.Speak(); // Θα καλέσει και τη μέθοδο από την Animal και τη νέα από την Dog
}
}
Αποτέλεσμα:
Rex κάνει έναν γενικό ήχο.
Rex γαβγίζει!
3. Σφράγιση Κλάσεων και Μεθόδων με το sealed
Μπορείς να αποτρέψεις την κληρονομικότητα μιας κλάσης ή τη δυνατότητα να υπερκαλυφθεί μια μέθοδος χρησιμοποιώντας τη λέξη-κλειδί sealed
.
- Αν μια κλάση είναι sealed, δεν μπορεί να έχει υποκλάσεις.
- Αν μια μέθοδος είναι sealed, δεν μπορεί να υπερκαλυφθεί από υποκλάσεις.
Παράδειγμα με sealed
:
// Αυτή η κλάση δεν μπορεί να κληρονομηθεί
sealed class FinalAnimal
{
public void MakeSound()
{
Console.WriteLine(“Τελικό ζώο κάνει ήχο.”);
}
}
// Μπορούμε να σφραγίσουμε και μεμονωμένες μεθόδους σε υποκλάσεις
class Animal
{
public virtual void Speak()
{
Console.WriteLine(“Ζώο κάνει ήχο.”);
}
}
class Dog : Animal
{
public sealed override void Speak()
{
Console.WriteLine(“Σκύλος γαβγίζει.”);
}
}
class SmallDog : Dog
{
// Δεν μπορούμε να υπερκαλύψουμε τη μέθοδο Speak από την Dog γιατί είναι sealed
// public override void Speak() { … } // Αυτό θα προκαλέσει σφάλμα
}
// Αφηρημένη κλάση Animal
abstract class Animal
{
public string Name { get; set; }
// Αφηρημένη μέθοδος, χωρίς υλοποίηση
public abstract void Speak();
}
// Υποκλάση Dog που υλοποιεί την αφηρημένη μέθοδο
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine($”{Name} γαβγίζει!”);
}
}
// Υποκλάση Cat που υλοποιεί την αφηρημένη μέθοδο
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine($”{Name} νιαουρίζει!”);
}
}
class Program
{
static void Main()
{
Animal dog = new Dog { Name = “Rex” };
Animal cat = new Cat { Name = “Milo” };
dog.Speak(); // Καλεί τη μέθοδο από την Dog
cat.Speak(); // Καλεί τη μέθοδο από την Cat
}
}
Αναλυτική Εξήγηση:
- Η
Animal
είναι μια αφηρημένη κλάση με μια αφηρημένη μέθοδοSpeak()
. Δεν μπορούμε να δημιουργήσουμε αντικείμενο απευθείας από τηνAnimal
. - Οι κλάσεις
Dog
καιCat
υλοποιούν τη μέθοδοSpeak()
με δικό τους τρόπο. - Ο πολυμορφισμός εμφανίζεται όταν δημιουργούμε αντικείμενα τύπου
Animal
, αλλά αυτά συμπεριφέρονται διαφορετικά ανάλογα με το αν είναιDog
ήCat
.
5. Προκαθορισμένες και Εικονικές Μέθοδοι
- Εικονικές Μέθοδοι (
virtual
): Επιτρέπουν στις υποκλάσεις να υπερκαλύπτουν τη συμπεριφορά τους, όπως στο παράδειγμα με τοSpeak()
που είδαμε παραπάνω. - Προκαθορισμένες Μέθοδοι (
override
): Η λέξη-κλειδίoverride
χρησιμοποιείται όταν μια υποκλάση θέλει να αλλάξει τη συμπεριφορά μιας εικονικής μεθόδου από την βασική κλάση.
Συμπέρασμα
Οι έννοιες της κληρονομικότητας και του πολυμορφισμού είναι πολύ σημαντικές για να κατανοήσεις πώς δουλεύουν οι κλάσεις και τα αντικείμενα στην C#. Με αυτές τις έννοιες, μπορείς να φτιάξεις ευέλικτο και επαναχρησιμοποιήσιμο κώδικα που μπορεί να αναπτυχθεί και να επεκταθεί εύκολα.
Σημαντικά σημεία που πρέπει να θυμάσαι:
- Κληρονομικότητα: Οι υποκλάσεις μπορούν να κληρονομήσουν ιδιότητες και μεθόδους από βασικές κλάσεις και να προσθέσουν νέες λειτουργίες.
- Πολυμορφισμός: Οι ίδιες μέθοδοι μπορούν να έχουν διαφορετική συμπεριφορά ανάλογα με την κλάση στην οποία υλοποιούνται.
- Αφηρημένες κλάσεις και προστατευμένες ιδιότητες είναι εργαλεία που προσθέτουν ευελιξία και ασφάλεια στον κώδικα.
- Το
sealed
εμποδίζει την κληρονομικότητα ή την υπερκάλυψη όταν το χρειάζεσαι.