Monimuotoisuus on olion kykyä muuntautua ohjelman suorituksen aikana käyttämään tilanteen mukaan omia tai jälkeläisensä ominaisuuksia ja/tai toimintoja. Tämä tarkoittaa yksinkertaisuudessaan sitä, että eri luokille voidaan määritellä saman nimisiä ominaisuuksia ja/tai toimintoja.
Tutkitaan yhdessä alla olevaa tilannetta, jossa on luotu Animal-yliluokka, josta on peritty Dog-, Cat- ja Chicken-luokat. Huomaa kuinka sekä Legs-ominaisuus että Talk()-toiminto on jätetty virtuaaliseksi.
Animal-luokkaan on määritelty virtuaalinen Legs-ominaisuus, jolle voidaan aliluokassa ylikirjoittaa eläinkohtaisesti uusi arvo. Samoin Animal-luokalla on yksi virtuaalinen Talk-metodi, jolloin sille voidaan ylikirjoittaa aliluokassa uusi toteutus käyttämällä override-määrettä.
Nyt Dog-, Cat ja Chicken-luokkia voidaan käyttää esimerksi seuraavasti. Huomaa kuinka Animal-luokan tyyppiseen olioon on sijoitettu aliluokan olio.
Animal-luokan olioita voidaan myös tehdä useita ja sisällyttää ne esim. List-rakenteeseen:
Ohjelma tulostaa silmukkarakenteesta:
Wuw!
Miau!
CotCot!
Wuw!
Miau!
CotCot!
Wuw!
Miau!
CotCot!
Monimuotoisuus tarjoaa keinon käyttää perintärakenteita siten, että olioille voidaan määritellä samannimisiä toimintoja. Edellisessä esimerkissä ei siis tarvittu kahta erinimistä toimintoa kertomaan miten koira ja kissa ääntelevät. Näin ollen esimerkiksi suurempaa joukkoa eläimiä voidaan käydä kätevästi läpi toistorakenteella ja käskeä kaikkia eläimiä ääntelemään omalla tavallaan.
Toinen esimerkki voisi olla vaikka piirrustusohjelma, jossa käyttäjä voi piirrellä useita erilaisia kuvioita: ympyröitä, neliöitä, viivoja, suorakulmioita, ... ja ne kaikki tulisi ottaa talteen ja piirtää näytölle myöhemmin. Kaikilla kuvioilla voisi olla yhteinen yliluokka, jossa olisi kuvioille yhteisiä ominaisuuksia ja virtuaalinen Piirra()-metodi, joka olisi ylikirjoitettu aliluokissa piirtämään kyseessä oleva kuvio näytölle. Kuvio-luokka ei sitä tiedä miten kuvio piirrettäisiin, mutta jokainen aliluokka (Nelio,Viiva,Ympyrä) osaisi sitten piirtää itsensä matemaattisesti oikein.
Aikaisemmissa esimerkeissä käsiteltiin vain virtuaalisten ominaisuuksien ja toimintojen ylikirjoittamista aliluokassa override-määreellä. Tuolloin yliluokan ominaisuudet ja toiminnot ylikirjoitettiin aliluokassa ja niitä käytetään olipa käyttävä objekti todellisuudessa yli- tai aliluokan objekti (monimuotoisuus). Jos aliluokassa käytetään new-määrettä ominaisuuden tai toiminnon kohdalla, niin silloin aliluokka ei ylikirjoita ominaisuutta/metodia, vaan piilottaa sen. Tällöin käytetään yliluokan ominaisuuksia tai toimintoja olipa käyttävä objekti todellisuudessa yli- tai aliluokan objekti. Tosin aliluokasta pääsee aina yliluokan metodiin käyttämällä base-avainsanaa: base.YliluokanMetodi().
Jos muuttaisimme aikaisemmin esitetyn Chicken-luokan Talk()-metodin käyttämään new-määrettä override-määreen sijasta:
public new void Talk()
{
Console.WriteLine("CotCot!");
}
Ja käyttäisimme monimuotoisuuden perusteella Animal-tunnisteessa Chicken-luokan oliota:
Animal animal = new Chicken();
animal.Talk();
Console.WriteLine("animal3 has {0} legs.", animal3.Legs);
Console.WriteLine("animal1 type is {0}.", animal3.GetType());
Tällöin Chicken-luokan Talk()-metodin käyttäminen Animal-tunnisteen sisällä käyttäisi Animal-luokan Talk()-metodia eikä Chicken-luokan Talk()-metodia.
Talk!
animal3 has 2 legs.
animal1 type is PolymorphismApplication.Chicken.
Kana ei enää kotkottele, vaan puhuu!
Alla olevassa esimerkissä on käytössä A-luokka, josta periytyvät luokat B ja C. Luokalla A on virtuaalinen Test()-methodi, joka ylikirjoitetaan override- ja new-määreillä aliluokissa B ja C. Luokkien olioita käytetään oman tunnisteen sisällä, jolloin ne käyttävät omia Test-metodejaan (rivit 25-27). Monimuotoisuuden nojalla luokan A tunnisteeseen on sijoitettu B-luokan olio, joka käyttää Test-metodissa new-määrettä (rivi 33,34). Tällöin olion käyttäminen piilottaa metodin ja käytetään todellisuudessa yliluokan metodia. Rivillä 36-37 luokan A tunnisteeseen sijoitetaan aliluokan eli luokan C olio, joka ylikirjoittaa A-luokassa olevan Test()-metodin. Tuolloin käytetään siis C-luokan Test()-metodia.
Virtual-määrettä käytetään yliluokan ominaisuuksissa ja/tai toiminnoissa silloin, kun halutaan toteuttaa uusi toteutus aliluokissa.
Override-määrettä käytetään laajentamaan/muuttamaan yliluokan ominaisuuksia/toimintoja aliluokassa. Sidonta tapahtuu sovelluksen suorituksen aikana ("runtime polymorphism" / "late binding")
New-määrettä käytetään piilottamaan yliluokan ominaisuuksia/toimintoja aliluokassa. Sidonta tapahtuu käännöksen aikana ("compile-time polymorphism" / "early binding").
Lisätietoa: Polymorphism (C# Programming Guide)