C# - Virtuális metódusok létrehozása osztályon belül

C# nyelv 14. rész

forráskód letöltése
Ebben a fejezetben olyan függvények létrehozásával próbálkozunk, melyeknek azonos a nevük a leszármazott osztályokban is, de azok mégsem felülírásra kerülnek. Megtudhatjuk azt is, hogy miként lehet egy adott osztályból az ősosztálybeli metódust elérni.
Ha létrehozunk egy osztályt és benne egy függvényt, majd ebből az osztályból származtatunk egy újat és abban szintén deklaráljuk az előbbi függvényt ugyanazzal a névvel, akkor ennek az lesz az eredménye, hogy elrejtjük az ős osztálybeli függvényt.
Ha viszont az ős osztályban található függvényt virtuálisként deklaráljuk a virtual kulcsszó megadásával, akkor a leszármazott osztályban lehetőségünk van felülírni azt. Ennek következményeként mindig az a függvény hívódik meg, amelyik osztály típusaként tároljuk az adott példányt.
Nézzük mindezt a gyakorlatban. Létrehozunk egy First nevű osztályt, benne két függvényt, az AA-t és BB-t. Mindkettő egy sztringet ad vissza, melyből megtudhatjuk, hogy melyik osztály, melyik függvényéről is van. Az AA egy hagyományos függvény, míg a BB-t virtuálisként deklaráljuk.
    class First
    {
      public string AA()
      {
        return "First.AA";
      }
      public virtual string BB()
      {
        return "First.BB";
      }
    }
Ezek után létrehozzuk a Second osztályt, a First-ből származtatva. Ebben szintén egy AA és BB függvényt deklarálunk. AA esetén nem teszünk semmit, így ez el fogja rejteni az ős osztálybeli AA függvényt. BB esetén az override kulcsszó használatával felülírjuk az ős osztályban található BB függvényt, melyet virtuálisként deklaráltunk. A BB-nél megfigyelhető még egy fontos dolog. A visszatérési értéknél nem csak az adott osztály, adott függvényére utaló sztringet adjuk vissza, hanem a base kulcsszó segítségével a First osztályban lévő BB függvényt is meghívjuk. A base használatával tehát mindig az adott osztály ősében lévő tagokat érhetjük el.
    class Second: First
    {
      public string AA()
      {
        return "Second.AA";
      }
      public override string BB()
      {
        return "Second.BB" + " " + base.BB();
      }
    }
Nézzük most hogyan viselkedik e két osztály a gyakorlatban. Hozzunk létre a First és a Second osztályból is egyet-egyet, de mindkét esetben egy First típusú változóba tároljuk el őket. Mivel egy osztály példánya eltárolható ősének típusán, így ez gond nélkül működik. Ezek után mind a két változónak meghívjuk az AA és BB függvényeit, hogy láthassuk, hogy valójában mely függvények kerülnek meghívásra.
      protected void button1_Click (object sender, System.EventArgs e)
      {
        First f = new First();
        First s = new Second();
        listBox1.Items.Add(f.AA());
        listBox1.Items.Add(f.BB());
        listBox1.Items.Add(s.AA());
        listBox1.Items.Add(s.BB());
      }
Az f.AA() hívása a First.AA sztringet szolgáltatja, tehát itt a First osztály AA függvénye futott. Ehhez hasonlóan a f.BB() hívása a First.BB szöveget adja, vagyis a First BB függvénye futott.
Amikor az s.AA() függvényt hívjuk, ismét a First.AA szöveget kapjuk, melyből az következik, hogy hiába hoztuk létre a Second típusú objektumot, az ha First típusban kerül tárolásra, akkor a nem virtuális függvénye tárolt típus osztályához tartozó függvényt aktivizálja.
Az s.BB() függvény hívásánál látható a virtuálisként deklarált függvények értelme. Ekkor a visszaadott sztringből következik, hogy valóban a Second osztály BB függvénye futott le. Vagyis hiába tároltuk a példányt First típusban és hívtuk meg a First BB függvényét, az ennek ellenére a Second osztály BB függvényét aktivizálta. Ebből látható, hogy ha virtuálisként deklarálunk egy függvényt egy osztályban, akkor az egyáltalán nem biztos, hogy a meghíváskor ténylegesen le is fut. Jelen esetben is az történt, hogy egy leszármazott osztálybeli függvény futott.
Következő példánkhoz hozzunk létre négy osztályt, mely mindegyike egymásból származik. Minden osztályban lesz egy Function nevű függvény, mely visszaadja az adott osztály nevét sztringként. Az első osztályban ezt a függvényt virtuálisként deklaráljuk, hogy a későbbiekben mindig felülírhassuk. Megfigyelhető, hogy a C osztályban ezt a függvényt a new módosítóval deklaráljuk és ismét a virtual kulcsszót használjuk. A new kulcsszó használatával elérhető, hogy újra deklaráljunk egy már meglévő ős osztálybeli metódust. Ennek az lesz a hatása, hogy a C osztálytól kezdve a Function függvény úgy viselkedik, mintha azt először a C osztályban hoztuk volna létre.
    class A
    {
      public virtual string Function() { return "A";}
    }
    class B: A
    {
      public override string Function() { return "B"; }
    }
    class C: B
    {
      new public virtual string Function() {return "C"; }
    }
    class D: C
    {
      public override string Function() { return "D"; }
    }
Ha ezek után létrehozunk egy példányt a negyedik D osztályból, majd ezt eltároljuk a másik három osztály típusán is, és sorra meghívjuk a Function függvényüket, akkor a következő történik: az A osztály típusán tárolt D osztály Function hívásakor valójában a B osztály függvénye fut. Ugyanez a helyzet, amikor a B osztály típusán tárolt D osztály Function-ját hívjuk. Amikor a C-t aktivizáljuk, akkor jelenik csak meg a D-hez tartozó D betű, köszönhető ez a C osztályban lévő new módosító kulcsszónak. Végül amikor a D függvényét hívjuk, akkor szintén a D betű jelenik meg.
      protected void button2_Click (object sender, System.EventArgs e)
      {
        D d = new D();
        A a = d;
        B b = d;
        C c = d;
        listBox1.Items.Add(a.Function());
        listBox1.Items.Add(b.Function());
        listBox1.Items.Add(c.Function());
        listBox1.Items.Add(d.Function());
      }
Ebből következik, hogy a virtualitás megszakítható és újra kezdhető az öröklési láncban, ha a new kulcsszót használjuk.

C# nyelv cikksorozat