Dela via


Polymorfism

Polymorfism kallas ofta för den tredje pelaren i objektorienterad programmering, efter inkapsling och arv. Polymorfism är ett grekiskt ord som betyder "mångaformade" och det har två distinkta aspekter:

  • Vid körning kan objekt i en härledd klass behandlas som objekt i en basklass på platser som metodparametrar och samlingar eller matriser. När den här polymorfismen inträffar är objektets deklarerade typ inte längre identisk med dess körningstyp.
  • Basklasser kan definiera och implementera virtuella metoder, och härledda klasser kan åsidosätta dem, vilket innebär att de tillhandahåller sin egen definition och implementering. När klientkoden anropar metoden vid körning letar CLR upp körningstypen för objektet och anropar den åsidosättningen av den virtuella metoden. I källkoden kan du anropa en metod i en basklass och orsaka att en härledd klasss version av metoden körs.

Med virtuella metoder kan du arbeta med grupper av relaterade objekt på ett enhetligt sätt. Anta till exempel att du har ett ritningsprogram som gör det möjligt för en användare att skapa olika typer av former på en rityta. Du vet inte vid kompileringstillfället vilka specifika typer av former som användaren ska skapa. Programmet måste dock hålla reda på alla olika typer av former som skapas och måste uppdatera dem som svar på användarens musåtgärder. Du kan använda polymorfism för att lösa problemet i två grundläggande steg:

  1. Skapa en klasshierarki där varje specifik formklass härleds från en gemensam basklass.
  2. Använd en virtuell metod för att anropa lämplig metod för alla härledda klasser via ett enda anrop till basklassmetoden.

Skapa först en basklass med namnet Shape, och härledda klasser som Rectangle, Circleoch Triangle. Shape Ge klassen en virtuell metod med namnet Drawoch åsidosätt den i varje härledd klass för att rita den specifika form som klassen representerar. Skapa ett List<Shape> objekt och lägg till , TriangleCircleoch Rectangle till det.

public class Shape
{
    // A few example members
    public int X { get; private set; }
    public int Y { get; private set; }
    public int Height { get; set; }
    public int Width { get; set; }

    // Virtual method
    public virtual void Draw()
    {
        Console.WriteLine("Performing base class drawing tasks");
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
        // Code to draw a circle...
        Console.WriteLine("Drawing a circle");
        base.Draw();
    }
}
public class Rectangle : Shape
{
    public override void Draw()
    {
        // Code to draw a rectangle...
        Console.WriteLine("Drawing a rectangle");
        base.Draw();
    }
}
public class Triangle : Shape
{
    public override void Draw()
    {
        // Code to draw a triangle...
        Console.WriteLine("Drawing a triangle");
        base.Draw();
    }
}

Om du vill uppdatera ritytan använder du en foreach-loop för att iterera genom listan och anropa Draw metoden för varje Shape objekt i listan. Även om varje objekt i listan har en deklarerad typ av Shapeär det körningstypen (den åsidosatta versionen av metoden i varje härledd klass) som anropas.

// Polymorphism at work #1: a Rectangle, Triangle and Circle
// can all be used wherever a Shape is expected. No cast is
// required because an implicit conversion exists from a derived
// class to its base class.
var shapes = new List<Shape>
{
    new Rectangle(),
    new Triangle(),
    new Circle()
};

// Polymorphism at work #2: the virtual method Draw is
// invoked on each of the derived classes, not the base class.
foreach (var shape in shapes)
{
    shape.Draw();
}
/* Output:
    Drawing a rectangle
    Performing base class drawing tasks
    Drawing a triangle
    Performing base class drawing tasks
    Drawing a circle
    Performing base class drawing tasks
*/

I C# är varje typ polymorf eftersom alla typer, inklusive användardefinierade typer, ärver från Object.

Översikt över polymorfism

Virtuella medlemmar

När en härledd klass ärver från en basklass innehåller den alla medlemmar i basklassen. Allt beteende som deklareras i basklassen är en del av den härledda klassen. Det gör att objekt i den härledda klassen kan behandlas som objekt i basklassen. Åtkomstmodifierare (public, protectedoch private så vidare) avgör om dessa medlemmar är tillgängliga från implementeringen av den härledda klassen. Virtuella metoder ger designern olika val för beteendet för den härledda klassen:

  • Den härledda klassen kan åsidosätta virtuella medlemmar i basklassen och definiera nytt beteende.
  • Den härledda klassen kan ärva den närmaste basklassmetoden utan att åsidosätta den, bevara det befintliga beteendet men aktivera ytterligare härledda klasser för att åsidosätta metoden.
  • Den härledda klassen kan definiera ny icke-virtuell implementering av de medlemmar som döljer basklassimplementeringarna.

En härledd klass kan endast åsidosätta en basklassmedlem om basklassmedlemmen deklareras som virtuell eller abstrakt. Den härledda medlemmen måste använda nyckelordet åsidosättning för att uttryckligen ange att metoden är avsedd att delta i virtuellt anrop. Följande kod innehåller ett exempel:

public class BaseClass
{
    public virtual void DoWork() { }
    public virtual int WorkProperty
    {
        get { return 0; }
    }
}
public class DerivedClass : BaseClass
{
    public override void DoWork() { }
    public override int WorkProperty
    {
        get { return 0; }
    }
}

Fält kan inte vara virtuella. endast metoder, egenskaper, händelser och indexerare kan vara virtuella. När en härledd klass åsidosätter en virtuell medlem anropas den medlemmen även när en instans av den klassen används som en instans av basklassen. Följande kod innehåller ett exempel:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = B;
A.DoWork();  // Also calls the new method.

Med virtuella metoder och egenskaper kan härledda klasser utöka en basklass utan att behöva använda basklassimplementeringen av en metod. Mer information finns i Versionshantering med åsidosättning och nya nyckelord. Ett gränssnitt ger ett annat sätt att definiera en metod eller uppsättning metoder vars implementering lämnas till härledda klasser.

Dölj basklassmedlemmar med nya medlemmar

Om du vill att din härledda klass ska ha en medlem med samma namn som en medlem i en basklass kan du använda det nya nyckelordet för att dölja basklassmedlemmen. Nyckelordet new placeras före returtypen för en klassmedlem som ersätts. Följande kod innehåller ett exempel:

public class BaseClass
{
    public void DoWork() { WorkField++; }
    public int WorkField;
    public int WorkProperty
    {
        get { return 0; }
    }
}

public class DerivedClass : BaseClass
{
    public new void DoWork() { WorkField++; }
    public new int WorkField;
    public new int WorkProperty
    {
        get { return 0; }
    }
}

Dolda basklassmedlemmar kan nås från klientkoden genom att instansen av den härledda klassen castas till en instans av basklassen. Till exempel:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.

Förhindra härledda klasser från att åsidosätta virtuella medlemmar

Virtuella medlemmar förblir virtuella, oavsett hur många klasser som har deklarerats mellan den virtuella medlemmen och den klass som ursprungligen deklarerade den. Om klassen A deklarerar en virtuell medlem och klassen B härleds från A, och klassen C härleds från B, ärver klassen C den virtuella medlemmen och kan åsidosätta den, oavsett om klassen B har deklarerat en åsidosättning för den medlemmen. Följande kod innehåller ett exempel:

public class A
{
    public virtual void DoWork() { }
}
public class B : A
{
    public override void DoWork() { }
}

En härledd klass kan stoppa virtuellt arv genom att deklarera en åsidosättning som förseglad. Om du stoppar arv måste nyckelordet sealed sättas före nyckelordet override i klassmedlemsdeklarationen. Följande kod innehåller ett exempel:

public class C : B
{
    public sealed override void DoWork() { }
}

I föregående exempel är metoden DoWork inte längre virtuell för någon klass som härletts från C. Det är fortfarande virtuellt för instanser av C, även om de är gjutna för att skriva B eller skriva A. Förseglade metoder kan ersättas av härledda klasser med hjälp av nyckelordet new , som följande exempel visar:

public class D : C
{
    public new void DoWork() { }
}

I det här fallet, om DoWork anropas med D hjälp av en variabel av typen D, anropas den nya DoWork . Om en variabel av typen C, B, eller A används för att komma åt en instans av Dföljer ett anrop till reglerna för DoWork virtuellt arv och dirigerar dessa anrop till implementeringen av DoWork på klassen C.

Få åtkomst till virtuella medlemmar i basklassen från härledda klasser

En härledd klass som har ersatt eller åsidosätter en metod eller egenskap kan fortfarande komma åt metoden eller egenskapen i basklassen med hjälp av nyckelordet base . Följande kod innehåller ett exempel:

public class Base
{
    public virtual void DoWork() {/*...*/ }
}
public class Derived : Base
{
    public override void DoWork()
    {
        //Perform Derived's work here
        //...
        // Call DoWork on base class
        base.DoWork();
    }
}

Mer information finns i bas.

Kommentar

Vi rekommenderar att virtuella medlemmar använder base för att anropa basklassimplementeringen av medlemmen i sin egen implementering. Genom att låta basklassbeteendet inträffa kan den härledda klassen koncentrera sig på att implementera beteende som är specifikt för den härledda klassen. Om basklassimplementeringen inte anropas är det upp till den härledda klassen att göra deras beteende kompatibelt med basklassens beteende.