Polimorfizm metod wirtualnych
Polimorfizm metod wirtualnych jest wielorakością występowania tych samych metod w klasach dziedziczących po klasie bazowej. Obiekt potomny ma możliwość innego zachowania niż rodzic dla wywołania tych samych funkcji. Polimorfizm metod wirtualnych jest dynamicznym przysłanianiem funkcji rodzica.
Zasada tworzenia metod wirtualnych
Metoda wirtualna w klasie bazowej rodzica zadeklarowana jest począwszy od słowa kluczowego virtual. Przykładowo tworzymy klasę bazową związaną z figurami płaskim. Każda figura płaska ma pole i obwód, ale inaczej liczone. Wstępna deklaracja tych metod w klasie rodzica może wyglądać jak poniżej
Wskazówka:
virtual public float Obwod() { return 0; }
virtual public float Pole() { return 0; }
virtual public float Przekatna() { return 0; }
virtual public float Srednica() { return 0; }
W klasach potomnych związanych z konkretną figurą płaską wykonuje się właściwe przysłonięcie metody, w tym przypadku zastosowanie poprawnego wzoru liczącego daną cechę figury. Przysłonięcie metody rozpoczyna się od słowa kluczowego override.
Na przykład dla okręgu metodę Pole zdefiniujemy jak poniżej
Wskazówka:
override public float Pole()
{
//nadpisz bazowa metodę licząca pole
//nową tożsamą ze wzorem pola okręgu
return (float)Math.PI*Odcinek[0]*Odcinek[0];
}
Zaś dla kwadratu będzie to poniższy kod
Wskazówka:
override public float Pole()
{
//nadpisz bazowa metodę licząca pole
//na nową tożsamą ze wzorem pola kwadratu
return Odcinek[0] * Odcinek[0];
}
Ćwiczenie:
Napisz aplikację, która będzie zawierać klasę bazową z metodami wirtualnymi pozwalającymi obliczyć obwód, pole, przekątną, średnicę, promień figury płaskiej. W projekcie utwórz klasy potomne klasy bazowej przeznaczone dla okręgu, kwadratu, trójkąta, prostokąta, rombu, trapezu. W klasach potomnych oprogramuj odpowiednie metody zdefiniowane w klasie bazowej (metody pozwalające obliczyć obwód, pole itp.)
Wymagania na klasę bazową
Ta część materiału jest kontynuacją tematu Klasa w języku C#
Klasa bazowa ma zawierać listę odcinków tworzących figurę płaską. Dla okręgu będzie to jeden odcinek, który jest promieniem. Ma zawierać prywatną zmienną do przechowywani nazwy figury płaskiej. Dostęp do tej zmiennej zrealizujemy metodą get/set
Wskazówka:
internal class ObiektPlaski
{
private int n;//ilość odcinków
private List<float> odcinek;
private String nazwa = "";
//metoda przypisuje i zwraca
//nazwę tworzonego obiektu
public String Nazwa {
set { nazwa = value; }
get { return nazwa; }
}
Propozycja metod wirtualnych wygląda jak poniżej
Wskazówka:
//definicja nagłówków wirtualnych metod przeznaczonych
//do nadpisania w klasach dziedziczących
virtual public float Obwod() { return 0; }
virtual public float Pole() { return 0; }
virtual public float Przekatna() { return 0; }
virtual public float Srednica() { return 0; }
virtual public void Promien(float R) { }
Pełen kod klasy bazowej wraz z proponowanym konstruktorem i destruktorem
Wskazówka:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FiguryPolimorfizmMetodWirtualnych
{
internal class ObiektPlaski
{
private int n;//ilość odcinków
private List<float> odcinek;
private String nazwa = "";
//metoda przypisuje i zwraca
//nazwę tworzonego obiektu
public String Nazwa {
set { nazwa = value; }
get { return nazwa; }
}
public int IleOdcinkow
{
set{n = value;//ustaw ile odcinkow ma obiekt plaski
odcinek.Clear();//czysc liste odcinkow
//ustaw dlugosci odcinkow na zero
for (int i = 0; i < n; i++) odcinek.Add(0);
}
get { return n; }//zwróć ilość odcinków
}
public List<float> Odcinek {
//metoda zwraca liste odcinków
//tworzacych obiekt plaski
get { return odcinek; }
}
//konstruktor
public ObiektPlaski(int ileOdcinkow)
{
odcinek=new List<float>();
IleOdcinkow = ileOdcinkow;
}
//destruktor
~ObiektPlaski()
{
odcinek.Clear();
odcinek = null;
}
//definicja nagłówków wirtualnych metod przeznaczonych
//do nadpisania w klasach dziedziczących
virtual public float Obwod() { return 0; }
virtual public float Pole() { return 0; }
virtual public float Przekatna() { return 0; }
virtual public float Srednica() { return 0; }
virtual public void Promien(float R) { }
}
}
Tworzymy klasę potomną dla okręgu
Wybierając menu Projekt/ Dodaj klasę? definiujemy klasę potomną o nazwie Okrag
Wewnątrz klasy Okrag utwórz konstruktor dla figury typu okrąg. Zwróć uwagę, że jest tylko jeden odcinek, i ten odcinek będzie przechowywać informację o długości promienia okręgu
Wskazówka:
public Okrag(int ileOdcinkow,float R) : base(ileOdcinkow)
{
//nowy konstruktor odpowiednio zmodyfikowany
//dla okręgu
//zerowy odcinek to promien
Odcinek[0] = R;
Nazwa="Okrąg";
}
Pamiętaj, że lista odcinków jest tworzona w konstruktorze klasy bazowej. W klasie potomnej konstruktor wymaga podania rozmiaru listy odcinków (czyli 1) oraz promienia okręgu (patrz w dalszej części kodu przy wywołaniu obiektu typu Okrag)
Zdefiniuj metody przysłaniające obliczanie pola, średnicy i obwodu okręgu
Wskazówka:
override public float Pole()
{
//nadpisz bazowa metodę licząca pole
//nową tożsamą ze wzorem pola okręgu
return (float)Math.PI*Odcinek[0]*Odcinek[0];
}
override public float Srednica()
{
//nadpisz bazową metodę liczącą pole
//nową tożsamą ze wzorem na średnicę okręgu
return 2 * Odcinek[0];
}
override public float Obwod()
{
//nadpisz bazową metodę liczącą obwód
//nową tożsamą ze wzorem na obwód okręgu
return 2 * (float)Math.PI* Odcinek[0];
}
Pełny kod klasy Okrag
Wskazówka:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FiguryPolimorfizmMetodWirtualnych
{
//
internal class Okrag : ObiektPlaski
{
public Okrag(int ileOdcinkow,float R) : base(ileOdcinkow)
{
//nowy konstruktor odpowiednio zmodyfikowany
//dla okręgu
//zerowy odcinek to promien
Odcinek[0] = R;
Nazwa="Okrąg";
}
override public float Pole()
{
//nadpisz bazowa metodę licząca pole
//nową tożsamą ze wzorem pola okręgu
return (float)Math.PI*Odcinek[0]*Odcinek[0];
}
override public float Srednica()
{
//nadpisz bazową metodę liczącą pole
//nową tożsamą ze wzorem na średnicę okręgu
return 2 * Odcinek[0];
}
override public float Obwod()
{
//nadpisz bazową metodę liczącą obwód
//nową tożsamą ze wzorem na obwód okręgu
return 2 * (float)Math.PI* Odcinek[0];
}
}
}
Tworzymy klasą potomną dla kwadratu
Powtórz kroki związane z dodawaniem nowej klasy do projektu. Nową klasę nazwij Kwadrat i wypełnij ją poniższym kodem
Wskazówka:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FiguryPolimorfizmMetodWirtualnych
{
internal class Kwadrat : ObiektPlaski
{
public Kwadrat(int ileOdcinkow, float a) : base(ileOdcinkow)
{
//nowy konstruktor odpowiednio zmodyfikowany
//dla kwadratu
//zerowy odcinek to bok
Odcinek[0] = a;
Nazwa = "Kwadrat";
}
override public float Pole()
{
//nadpisz bazowa metodę licząca pole
//na nową tożsamą ze wzorem pola kwadrau
return Odcinek[0] * Odcinek[0];
}
override public float Obwod()
{
//nadpisz bazową metodę liczącą obwód
//na nową tożsamą ze wzorem na obwód kawdratu
return 4 * Odcinek[0];
}
override public float Przekatna()
{
//nadpisz bazową metodę liczącą przekatną
//na nową tożsamą ze wzorem na przekatną kawdratu
return Odcinek[0]*(float)Math.Pow(2,0.5);
}
}
}
Tworzymy formatkę do obecnego projektu aplikacji
Na formatce umieść kontrolki pozwalające zdefiniować dowolny okrąg i kwadrat. Do wyprowadzania wyników może posłużyć jedna wspólna kontrolka typu TextBox ustawiona do pracy w układzie wielu linii
W zdarzeniu Click odpowiednich kontrolek typu Button zdefiniuj lokalne zmienne typu Okrag i Kwadrat, i zwróć informacje o ich podstawowych właściwościach
Proponowany kod dla okręgu
Wskazówka:
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text.Length < 1) return;
float r = (float)Convert.ToDouble(textBox1.Text);
Okrag o = new Okrag(1, r);
textBox2.Clear();
textBox2.AppendText("UTWORZONO "+o.Nazwa + Environment.NewLine);
textBox2.AppendText("PODSTAWOWE DANE:"+ Environment.NewLine);
textBox2.AppendText("Promień = " + o.Odcinek[0].ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Średnica = " + o.Srednica().ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Obwód = " + o.Obwod().ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Pole = " + o.Pole().ToString("F2")
+ Environment.NewLine);
}
Proponowany kod dla kwadratu
Wskazówka:
private void button2_Click(object sender, EventArgs e)
{
if (textBox3.Text.Length < 1) return;
float a = (float)Convert.ToDouble(textBox3.Text);
Kwadrat k = new Kwadrat(1, a);
textBox2.Clear();
textBox2.AppendText("UTWORZONO " + k.Nazwa + Environment.NewLine);
textBox2.AppendText("PODSTAWOWE DANE:" + Environment.NewLine);
textBox2.AppendText("Bok = " + k.Odcinek[0].ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Obwód = " + k.Obwod().ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Pole = " + k.Pole().ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Przekątna = " + k.Przekatna().ToString("F2")
+ Environment.NewLine);
}
Pełny kod klasy Form1.cs
Wskazówka:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FiguryPolimorfizmMetodWirtualnych
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text.Length < 1) return;
float r = (float)Convert.ToDouble(textBox1.Text);
Okrag o = new Okrag(1, r);
textBox2.Clear();
textBox2.AppendText("UTWORZONO "+o.Nazwa + Environment.NewLine);
textBox2.AppendText("PODSTAWOWE DANE:"+ Environment.NewLine);
textBox2.AppendText("Promień = " + o.Odcinek[0].ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Średnica = " + o.Srednica().ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Obwód = " + o.Obwod().ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Pole = " + o.Pole().ToString("F2")
+ Environment.NewLine);
}
private void button2_Click(object sender, EventArgs e)
{
if (textBox3.Text.Length < 1) return;
float a = (float)Convert.ToDouble(textBox3.Text);
Kwadrat k = new Kwadrat(1, a);
textBox2.Clear();
textBox2.AppendText("UTWORZONO " + k.Nazwa + Environment.NewLine);
textBox2.AppendText("PODSTAWOWE DANE:" + Environment.NewLine);
textBox2.AppendText("Bok = " + k.Odcinek[0].ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Obwód = " + k.Obwod().ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Pole = " + k.Pole().ToString("F2")
+ Environment.NewLine);
textBox2.AppendText("Przekątna = " + k.Przekatna().ToString("F2")
+ Environment.NewLine);
}
}
}
Zadanie
Rozbuduj aplikację o pozostałe figury: trójkąt, romb, trapez.