Megosztás a következőn keresztül:


Polimorfizmus

A polimorfizmust gyakran nevezik az objektumorientált programozás harmadik pillérének a beágyazás és az öröklés után. A polimorfizmus egy görög szó, amely "többalakú" szót jelent, és két különböző aspektusa van:

  • Futtatáskor a származtatott osztály objektumai egy alaposztály objektumaként kezelhetők olyan helyeken, mint a metódusparaméterek, gyűjtemények vagy tömbök. Ha ez a polimorfizmus bekövetkezik, az objektum deklarált típusa már nem azonos a futásidejű típusával.
  • Az alaposztályok meghatározhatnak és implementálhatnak virtuális metódusokat, a származtatott osztályok pedig felülbírálhatják őket, ami azt jelenti, hogy saját definíciót és implementációt biztosítanak. Futásidőben, amikor az ügyfélkód meghívja a metódust, a CLR megkeresi az objektum futásidejének típusát, és meghívja a virtuális metódus felülbírálását. A forráskódban meghívhat egy metódust egy alaposztályon, és a metódus származtatott osztályának verzióját hajthatja végre.

A virtuális módszerek lehetővé teszik a kapcsolódó objektumok csoportjainak egységes használatát. Tegyük fel például, hogy van egy rajzalkalmazása, amely lehetővé teszi a felhasználó számára, hogy különféle alakzatokat hozzon létre a rajzfelületen. Fordításkor nem tudja, hogy a felhasználó milyen típusú alakzatokat fog létrehozni. Az alkalmazásnak azonban nyomon kell követnie az összes létrehozott alakzattípust, és frissítenie kell őket a felhasználói egérműveletekre válaszul. A probléma megoldásához két alapvető lépésben használhatja a polimorfizmust:

  1. Hozzon létre egy osztályhierarchiát, amelyben minden egyes alakzatosztály egy közös alaposztályból származik.
  2. Virtuális metódus használatával meghívhatja a megfelelő metódust bármely származtatott osztályon az alaposztály metódusának egyetlen hívásával.

Először hozzon létre egy alaposztályt , és Shapehozzon létre olyan származtatott osztályokat, mint az Rectangle, Circleés Triangle. Adjon meg egy virtuális metódust az Shape osztálynak Draw, és bírálja felül minden származtatott osztályban, hogy megrajzolja az osztály által képviselt alakzatot. Hozzon létre egy objektumot List<Shape> , és adjon hozzá egy Circle, Triangleés Rectangle hozzá.

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();
    }
}

A rajzfelület frissítéséhez használjon egy foreach hurkot a lista iterálásához, és hívja meg a Draw metódust a lista minden objektumánShape. Annak ellenére, hogy a lista minden objektumának Shapedeklarált típusa van, a meghívandó futásidejű típus (a metódus felülírt verziója az egyes származtatott osztályokban).

// 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
*/

A C#-ban minden típus polimorfikus, mert minden típus, beleértve a felhasználó által definiált típusokat is, öröklődik.Object

A polimorfizmus áttekintése

Virtuális tagok

Ha egy származtatott osztály örökli az alaposztályt, az az alaposztály összes tagját tartalmazza. Az alaposztályban deklarált összes viselkedés a származtatott osztály része. Ez lehetővé teszi, hogy a származtatott osztály objektumai az alaposztály objektumaiként legyenek kezelve. A hozzáférési módosítók (publicés így tovább) határozzák meg, protectedprivate hogy ezek a tagok elérhetők-e a származtatott osztály implementációjából. A virtuális metódusok különböző választási lehetőségeket adnak a tervezőnek a származtatott osztály viselkedéséhez:

  • A származtatott osztály felülírhatja az alaposztály virtuális tagjait, és új viselkedést határozhat meg.
  • A származtatott osztály anélkül örökölheti a legközelebbi alaposztály-metódust, hogy felülbírálja azt, megőrizve a meglévő viselkedést, de lehetővé teszi a további származtatott osztályok számára a metódus felülbírálását.
  • A származtatott osztály definiálhatja azon tagok új, nem virtuális implementációit, amelyek elrejtik az alaposztály-implementációkat.

A származtatott osztály csak akkor bírálhat felül egy alaposztálytagot, ha az alaposztálytag virtuális vagy absztraktként van deklarálva. A származtatott tagnak a felülbírálási kulcsszóval kell egyértelműen jeleznie, hogy a metódus a virtuális meghívásban való részvételre szolgál. Az alábbi kód egy példát mutat be:

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; }
    }
}

A mezők nem lehetnek virtuálisak; csak metódusok, tulajdonságok, események és indexelők lehetnek virtuálisak. Ha egy származtatott osztály felülbírál egy virtuális tagot, a rendszer akkor is meghívja ezt a tagot, ha az adott osztály egy példánya az alaposztály példányaként érhető el. Az alábbi kód egy példát mutat be:

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

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

A virtuális metódusok és tulajdonságok lehetővé teszik a származtatott osztályok számára az alaposztályok kiterjesztését anélkül, hogy egy metódus alaposztály-implementációját kellene használniuk. További információ: Verziószámozás felülbírálással és új kulcsszavakkal. Az interfész egy másik módot kínál egy metódus vagy metóduskészlet meghatározására, amelynek implementációja származtatott osztályokra van hagyva.

Alaposztálytagok elrejtése új tagokkal

Ha azt szeretné, hogy a származtatott osztály az alaposztály tagjával azonos nevű taggal rendelkezzen, az új kulcsszóval elrejtheti az alaposztály tagját. A new kulcsszó a lecserélt osztálytag visszatérési típusa elé kerül. Az alábbi kód egy példát mutat be:

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; }
    }
}

A rejtett alaposztálytagok az ügyfélkódból úgy érhetők el, hogy a származtatott osztály példányát az alaposztály egy példányára válogatják. Példa:

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

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

A származtatott osztályok nem bírálják felül a virtuális tagokat

A virtuális tagok virtuálisak maradnak, függetlenül attól, hogy hány osztály lett deklarálva a virtuális tag és az eredetileg deklarált osztály között. Ha az osztály A egy virtuális tagot deklarál, és az osztály B származik A, és az osztály C származik belőle B, az osztály C örökli a virtuális tagot, és felülbírálhatja azt, függetlenül attól, hogy az osztály B felülbírálást deklarált-e az adott tag számára. Az alábbi kód egy példát mutat be:

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

A származtatott osztály leállíthatja a virtuális öröklést, ha lezártként deklarál egy felülbírálást. Az öröklés leállításához a sealed kulcsszót a override kulcsszó elé kell helyezni az osztálytag-deklarációban. Az alábbi kód egy példát mutat be:

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

Az előző példában a metódus DoWork már nem virtuális a forrásból Cszármaztatott osztály számára. A példányok Cesetében továbbra is virtuális, még akkor is, ha beírásra B vagy beírásra vannak öntöttve A. A lezárt módszereket a kulcsszó használatával new származtatott osztályok helyettesíthetik, ahogy az alábbi példa is mutatja:

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

Ebben az esetben, ha DoWork egy típusváltozót DhasználD, az újat DoWork hívja meg a rendszer. Ha egy változó típusú C, Bvagy A egy példány Delérésére szolgál, a hívás a virtuális öröklés szabályainak követésére DoWork szolgál, és a hívásokat az osztály Cimplementálásához DoWork irányítja.

Az alaposztály virtuális tagjainak elérése származtatott osztályokból

Egy olyan származtatott osztály, amely lecserélt vagy felülírt egy metódust vagy tulajdonságot, továbbra is hozzáférhet az alaposztály metódusához vagy tulajdonságához a base kulcsszó használatával. Az alábbi kód egy példát mutat be:

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();
    }
}

További információ: alap.

Feljegyzés

Javasoljuk, hogy a virtuális tagok saját base implementációjukban hívják meg az adott tag alaposztály-implementációját. Az alaposztály viselkedésének engedélyezése lehetővé teszi, hogy a származtatott osztály a származtatott osztályra jellemző viselkedés implementálására összpontosítson. Ha az alaposztály implementációját nem hívják meg, a származtatott osztályon múlik, hogy a viselkedésük kompatibilis legyen az alaposztály viselkedésével.