Oto, czym różnią się funkcje abstrakcyjne od wirtualnych:
- funkcja wirtualna może zostać nadpisana przez klasę dziedziczącą, ale nie musi. Funkcja abstrakcyjna musi zostać zaimplementowana w klasie dziedziczącej (chyba, że klasa dziedzicząca także jest abstrakcyjna)
- funkcja abstrakcyjna nie ma ciała, funkcja wirtualna ma
- funkcję abstrakcyjną możemy mieć tylko w klasie abstrakcyjnej, funkcję wirtualną w dowolnej klasie niestatycznej (modyfikatory abstract i virtual oraz static przeczą sobie nawzajem - więcej o klasach i metodach statycznych tutaj [under construction])
Ani funkcja wirtualna, ani abstrakcyjna nie mogą być private. Wynika to ze zdrowego rozsądku - jak klasa dziedzicząca miałaby je nadpisać, jeśli nie ma do nich dostępu?
abstract class AbstractClass { private abstract void AbstractMethod(); //błąd kompilacji private virtual void VirtualMethod() //błąd kompilacji { } }
Funkcja abstrakcyjna nie może posiadać implementacji:
abstract class AbstractClass { protected abstract void AbstractMethod() //błąd kompilacji { } }
Metoda wirtualna musi mieć implementację:
abstract class AbstractClass { protected virtual void VirtualMethod(); //błąd kompilacji }
Funkcja abstrakcyjna musi być w klasie abstrakcyjnej:
class AbstractClass { protected abstract void AbstractMethod(); //błąd kompilacji }
Funkcja abstrakcyjna musi być zaimplementowana w nieabstrakcyjnej klasie dziedziczącej, funkcja wirtualna nie musi:
abstract class AbstractClass { protected abstract void AbstractMethod(); protected virtual void VirtualMethod() { } } class NonAbstractClass : AbstractClass //błąd kompilacji - brak implementacji AbstractMethod { }
W klasach statycznych ani funkcje abstrakcyjne, ani wirtualne nie mają racji bytu:
static class StaticClass { public virtual void VirtualMethod() //błąd kompilacji - w klasie statycznej mogą byc tylko statyczne funkcje { } }
Nienadpisanie funkcji wirtualnej jest całkowicie dozwolone:
public abstract class BaseClass { public virtual void VirtualMethod() { Console.WriteLine("A"); } public abstract void AbstractMethod(); } public class ChildClass : BaseClass { //brak nadpisania funkcji wirtualnej jest ok //public override void VirtualMethod() //{ // Console.WriteLine("B"); //} public override void AbstractMethod() { Console.WriteLine("Implementing abstract method"); } }
Musimy pamiętać o użyciu słowa kluczowego override. Bez niego, w przypadku klasy abstrakcyjnej, kompilacja nie powiedzie się, ponieważ metoda abstrakcyjna nie jest zaimplementowana. Bez słowa override tworzymy nową metodę, zasłaniającą starą.
public abstract class BaseClass { public abstract void AbstractMethod(); } public class ChildClass : BaseClass { public void AbstractMethod() //uwaga - brak słowa kluczowego override { Console.WriteLine("B"); } }
W przypadku funkcji wirtualnej kompilacja powiedzie się, ale nowa metoda zasłoni starą metodę. Jej wywołanie nie będzie więc wirtualne:
public class BaseClass { public virtual void VirtualMethod() { Console.WriteLine("A"); } } public class ChildClass : BaseClass { public void VirtualMethod() //uwaga - brak słowa kluczowego override { Console.WriteLine("B"); } } class Program { static void Main(string[] args) { BaseClass baseObject = new ChildClass(); baseObject.VirtualMethod(); //wypisze się A Console.ReadKey(); } }
Visual Studio słusznie podpowiada nam w tym przypadku, że jeśli chcemy faktycznie przesłonić metodę, powinniśmy użyć słowa kluczowego new. W przeciwnym przypadku powinniśmy użyć słowa override. Po dopisaniu override metoda wołana jest w sposób wirtualny.
public class BaseClass { public virtual void VirtualMethod() { Console.WriteLine("A"); } } public class ChildClass : BaseClass { public override void VirtualMethod() { Console.WriteLine("B"); } } class Program { static void Main(string[] args) { BaseClass baseObject = new ChildClass(); baseObject.VirtualMethod(); //wypisze się B Console.ReadKey(); } }
Jedyną okolicznością kiedy nie musimy implementować metody abstrakcyjnej w klasie dziedziczącej jest sytuacja kiedy klasa dziedzicząca sama jest abstrakcyjna.
public abstract class BaseClass { public abstract void VirtualMethod(); } public abstract class ChildClass : BaseClass //ok { }
Komentarze
Prześlij komentarz