Polimorfizm czasu kompilacji a czasu wykonania
Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 25 z 70.
Polimorfizm oznacza „wiele form” — zdolność tego samego kodu do różnego działania w zależności od kontekstu. C# obsługuje dwa odrębne typy polimorfizmu, a zrozumienie, kiedy każdy z nich jest rozstrzygany, pomaga pisać bardziej elastyczny kod.
Polimorfizm czasu kompilacji (nazywany również polimorfizmem statycznym) jest rozstrzygany przez kompilator przed uruchomieniem programu. Przeciążanie metod to główny przykład — wiele metod ma tę samą nazwę, ale różne parametry:
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // Kompilator wybiera wersję int
calc.Add(5.0, 3.0); // Kompilator wybiera wersję doubleKompilator analizuje typy argumentów i wybiera właściwą metodę podczas kompilacji. Ta decyzja zostaje podjęta przed uruchomieniem programu.
Polimorfizm czasu wykonania (polimorfizm dynamiczny) jest rozstrzygany podczas działania programu. Dzieje się tak w przypadku metod virtual i override — rzeczywisty typ obiektu określa, która metoda zostanie wykonana:
Animal pet = new Dog(); // Zadeklarowany jako Animal, w rzeczywistości jest Dog
pet.Speak(); // Wywołuje metodę Speak() klasy Dog w czasie działania programuKompilator nie wie, które Speak() zostanie uruchomione — ta decyzja zapada w czasie wykonywania programu na podstawie rzeczywistego typu obiektu. To właśnie sprawia, że polimorfizm oparty na dziedziczeniu jest tak potężnym narzędziem do tworzenia elastycznych i rozszerzalnych systemów.
Wyzwanie
ŁatwyZbudujmy system konwersji, który pokaże w praktyce oba rodzaje polimorfizmu. Utworzysz klasę wykorzystującą przeciążanie metod (polimorfizm czasu kompilacji), a także hierarchię dziedziczenia wykorzystującą metody virtual/override (polimorfizm czasu wykonywania).
Rozmieścisz swój kod w czterech plikach:
Converter.cs: Zdefiniuj klasęConverterw przestrzeni nazwConversion. Ta klasa demonstruje polimorfizm czasu kompilacji przez przeciążanie metod. Utwórz trzy przeciążone metodyConvert:Convert(int value)zwraca"Integer: {value}"Convert(double value)zwraca"Double: {value}"Convert(string value)zwraca"String: {value}"
Shape.cs: Zdefiniuj klasę bazowąShapew przestrzeni nazwConversion. Ta klasa ma właściwośćName(string) oraz konstruktor, który ją ustawia. Dodaj metodęvirtualo nazwieDescribe(), która zwraca"This is a {Name}".Circle.cs: Zdefiniuj klasęCirclew przestrzeni nazwConversion, która dziedziczy poShape. Dodaj właściwośćRadius(double). Konstruktor przyjmuje promień i przekazuje"Circle"do konstruktora bazowego. NadpiszDescribe(), aby zwracała"This is a Circle with radius {Radius}".Program.cs: W głównym pliku zademonstruj oba rodzaje polimorfizmu. Najpierw utwórz obiektConverteri wywołaj wszystkie trzy przeciążone metody z wartościami wejściowymi. Następnie utwórz obiektCircle, przechowaj go w zmiennej typuShapei wywołajDescribe(), aby pokazać polimorfizm czasu wykonywania.
Otrzymasz cztery dane wejściowe:
- Wartość całkowitą
- Wartość typu double
- Wartość tekstową
- Promień okręgu (double)
Wypisz wynik w następującym formacie:
Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}Na przykład, jeśli dane wejściowe to 42, 3.14, Hello i 5.5, wynik powinien wyglądać następująco:
Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5Zwróć uwagę na kluczową różnicę: kompilator decyduje, którą metodę Convert wywołać na podstawie typów argumentów w czasie kompilacji, natomiast metoda Describe() jest rozstrzygana w czasie wykonywania na podstawie rzeczywistego typu obiektu, nawet jeśli jest on przechowywany w zmiennej typu Shape!
Spróbuj swoich sił
using System;
using Conversion;
class Program
{
public static void Main(string[] args)
{
// Wczytaj dane wejściowe
int intValue = Convert.ToInt32(Console.ReadLine());
double doubleValue = Convert.ToDouble(Console.ReadLine());
string stringValue = Console.ReadLine();
double radius = Convert.ToDouble(Console.ReadLine());
// TODO: Zademonstruj polimorfizm statyczny
// Utwórz obiekt Converter i wywołaj wszystkie trzy przeciążone metody Convert
Console.WriteLine("Compile-time Polymorphism:");
// Wywołaj Convert z argumentami intValue, doubleValue i stringValue
// TODO: Zademonstruj polimorfizm dynamiczny
// Utwórz obiekt Circle i przypisz go do zmiennej typu Shape
// Następnie wywołaj Describe() na zmiennej typu Shape
Console.WriteLine("Runtime Polymorphism:");
// Utwórz obiekt Circle i wywołaj Describe()
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrznePrzestrzenie nazw i dyrektywyWprowadzenie do klas i obiektówSłowo kluczowe „this”Metody i parametryPola a właściwościKonstruktoryInicjalizatory obiektówPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawowa składnia dziedziczenia (:)Słowo kluczowe 'base'Słowa kluczowe virtual i overrideKlasy sealedBazowa klasa 'object'Powtórzenie – hierarchia pracowników7Zaawansowane funkcje
Przeciążanie operatorówIndeksery (this[])Nadpisywanie ToString()Metody rozszerzającePodsumowanie – własna lista2Właściwości i składowe statyczne
Właściwości automatyczneWłaściwości tylko do odczytu i zapisuPola i metody statyczneKlasy statyczneSkładowe z ciałem wyrażeniowym5Polimorfizm i interfejsy
Polimorfizm czasu kompilacji a czasu wykonaniaInterfejs a klasa abstrakcyjnaWiele interfejsówJawne implementowanie interfejsówRzutowanie w górę i w dółPowtórka — kalkulator kształtów8Zaawansowane koncepcje OOP
Kompozycja zamiast dziedziczeniaTypy generyczne (klasy i metody)Delegaty i zdarzeniaAtrybuty i refleksjaIDisposable i instrukcja usingPodstawy wstrzykiwania zależności11Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu3Architektura klas
Dane instancji a dane statyczneSłowa kluczowe 'readonly' i 'const'Pola zapasowePowtórzenie — menedżer kont bankowych6Hermetyzacja
Modyfikatory dostępuWłaściwości w hermetyzacjiImplementacja ukrywania danychWzorce niezmiennościPowtórka — dane studentów9Argumenty zmiennej liczby
Słowo kluczowe „params”Parametry opcjonalneArgumenty nazwanePrzeciążanie metodPoćwicz samodzielnie: Kompilator C# online