Polymorfism
Definition Polymorfism är en teknik för att dynamiskt kunna välja vilken metod som ska exekveras under programkörning istället för under kompilering. Varje objekt innehåller information om vilken klass det tillhör (en pekare till klassen). Om det finns metoder med samma namn i klasserna inom en arvshierarki, så väljs rätt metod automatiskt. Polymorfism tillåter en referens till basklassen att också referera till en instans av godtyckligt härledd klass. Med andra ord, en variabel av basklasstyp kan kopplas till ett objekt av härledd klassen.
Polymorfism Person Anställd Student Kund Chef Lärare Säljare Adress private Person person; person = new Anställd(); person = new Chef(); Polymorfism betyder att objektets typ som styr och inte referenstypen. Idetta fall det är ”Anställd”-objektet och ”Chef”-objektet som styr och inte ”Person”-referensen(person) som styr. En Person behöver inte vara Chef men en Chef är en Person och därmed Person har inte Chefens alla fält. private Chef chef; chef = new person(); // Fel
Virtuella metoder När en metod anropas via en referens av basklasstyp, men för ett objekt av härledd typ- vilken metod ska då väljas, referensens eller det faktiska objektets? Kompilatorn kan inte förutsätta samtliga fall, det enda den kan se är referensens typ statisk bindning som är default i C#. För att få ”rätt” metod anropad måste någon liten procedur hitta den klass varifrån objektet kommer, och anropa den klassens metod dynamisk bindning. I C# måste explicit anges om en metod ska anropas med dynamisk bindning genom att göra den virtual. Polymorfism åstadkoms alltså med hjälp av virtuella metoder. Nyckelordet virtual flaggar att detta är den första implementeringen av en metod och att om samma metod med samma signatur finns i härledd klass då är det tillåtet att omdefiniera Implementationen.
Virtuella metoder Shape Rectangle Circle Area() class Shape { private int x, y; public Shape() {} public Shape(int x, int y) { this.x = x; this.y = y; } public virtual double Area() { Console.Write(”Shape Area”); return 0.0; } } Man måste uttryckligen använda nyckelordet virtual för att applicera polymorfism på en metod. I detta exempel vi flaggar för kompilatorn att just metoden Area kan anropas med dynamisk bindning.
override metoder Nyckelordet override ger möjlighet att låta en härledd klass ha sin egen implementering av en metod som också finns i basklassen och som är deklarerad virtual. Det finns några viktiga regler som man måste följa när man deklarerar polymorfa metoder med hjälp av nyckelorden virtual och override: Det är inte tillåtet att deklarera en privat metod med nyckelorden virtual eller override. De två metoderna måste vara identiska. Det innebär att de måste ha samma namn, samma parametertyper och samma returtyp. Man kan bara overrida en virtual metod. Om basklassmetoden inte är virtual, och man försöker overrida den, resulterar det i ett kompileringsfel. Om den härledda klassen deklarerar en metod utan nyckelordet override, så kan den inte overrida basklassens metod. Det blir istället en implementering av en helt annan metod som råkar ha samma namn. Detta kommer att genererar en gömningsvarning vid kompileringen, som man kan undvika genom att använda nyckelordet new. En override metod är implicit virtual och kan själv overridas i ytterligare en härledd klass. Det är dock inte tillåtet att uttryckligen deklarera en override metod som virtuell med hjälp av nyckelordet virtual.
virtual, override metoder Shape Area() Rectangle Circle class Circle: Shape { private int radius; public Circle(int x, int y, int radius): base(x,y) { this.radius = radius } public override double Area() // beräkan area { Console.Write(”Circle Area”); return System.Math.PI * radius * radius; } } class Shape { private int x, y; public Shape() {} public Shape(int x, int y) { this.x = x; this.y = y; } public virtual double Area() { Console.Write(”Shape Area”); return 0.0; } } class Rectangle: Shape { public Rectangle(int x, int y, int radius): base(x,y) { } public override double Area() // beräkan area { Console.Write(”Rectangle Area”); return x * y; } }
Bindningstyper Med bindning menas att en metod knyts till ett objekt. Metoden som används kan vara i objektets klass i basklassen Statisk bindning: bindningen sker vid kompileringstillfället. Dynamisk bindning: bindningen sker vid programkörningen. Dynamisk bindning uppnås med virtuella metoder.
Metoder för olika bindningstyper Statisk Varje klass har sina egna metoder virtual En metod i en härledd klass kan ersätta metoden i basklassen med samma namn. Abstract En metod i en härledd klass måste ersätta metoden i basklassen med samma namn. Metoden i basklassen implementeras inte.
Fördelar med statisk kontra dynamisk metodbinding Effektivitet Statisk bindning använder färre CPU-cykler, dvs resurssnålare och snabbare än dynamisk bindning Felhantering Statisk bindning gör det möjligt att fånga fel direkt vid kompilering, snarare än vid exekvering Flexibilitet Dynamisk bindning ökar flexibiliteten, medan statisk bindning försvårar återanvändning
new-metoder Om en basklass och en härledd klass innehåller två metoder med samma signatur, alltså med samma metodsnamn samma antal parametrar och typ, så är dessa metoder helt orelaterade till varandra. class Shape { public double Area() return 0.0; } class Circle : Shape { public double Area() return System.Math.PI * radius * radius; } Shape Area() Rectangle Circle Om man kompilerar exemplen ovanför, så kommer kompilatorn att generera ett varningsmeddelande som säger att: Circle.Area() gömmer Shape.Area()
new-metoder Varningen är en påminnelse om att eftersom de två metoderna har samma signatur, och är orelaterade till varandra, så kommer definitionen av Area i Circle att gömma metoden Area i Shape. I bästa fall är ett sådant sammanträffande bara är en källa till förvirring och man bör överväga att döpa om metoderna för att undvika kollisionen. new är ett annat vanligt nyckelord som kan ses med metoder. Det anger att en basklassmetod ska, i klassen, gömmas bakom en metod med samma namn. class Shape { public void Draw() } class Circle : Shape { public new void Draw() } Shape Draw() Rectangle Circle
new-metoder Användandet av nyckelordet new ändrar dock inte det faktum att de två metoderna är fortfarande orelaterade till varandra och att gömningen fortfarande sker. Det är bara varningen som stängs av. För att komma åt en gömd metod måste base användas. class Shape { public void Draw() } class Circle : Shape { public new void Draw() base.Draw(); } Shape Draw() Rectangle Circle