Przejdź do głównej zawartości

[C#] Czym się różnią funkcje abstrakcyjne od wirtualnych?


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])
Przyjrzyjmy się bliżej tym dwóm typom funkcji.

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