1. 程式人生 > >virtual、abstract、override與多型

virtual、abstract、override與多型

abstract與override

using System;

public class Test
{   
    public static void Main()
    {
        Animal dog = new Dog("dog");
        Animal cat = new Cat("cat");
        dog.Shout();
        cat.Shout();    
    }
}

abstract class Animal
{
    public Animal(string name){this.name = name;}
    public
string name = "Animal"; public abstract void Shout(); } class Dog : Animal { public Dog(string name) : base(name){} public override void Shout() { Console.WriteLine("Dog 汪!"); } } class Cat : Animal { public Cat(string name) : base(name){} public override void
Shout() { Console.WriteLine("Cat 喵!"); } }

輸出結果為:

Dog!
Cat!

理解:

  • 父類的引用指向子類的例項,若呼叫其父類方法且該方法在子類中被重寫,則實際呼叫的是子類中重寫過的方法
  • 因為抽象類中的Shout方法是抽象方法,不能有方法體,不可能呼叫父類Animal中的Shout方法,所以很好理解。

其他補充:

  1. 抽象類可以有建構函式,雖然抽象類不可以例項化,但是可用於為其派生類在例項化之前進行一些公共的初始化操作(賦值欄位、執行一些公共邏輯等)
  2. 在本例中,需要為Animal的派生類Dog、Cat實現建構函式,因為在Main函式中new操作符之後的派生類建構函式需要一個string引數。

virtual與override

using System;

public class Test
{   
    public static void Main()
    {
        Animal dog = new Dog("dog");
        Animal cat = new Cat("cat");
        dog.Shout();
        cat.Shout(); 

        Animal animal = new Animal("animal"); 
        Dog dog2 = new Dog("dog2");   
        animal.Shout();
        dog2.Shout(); 
    }
}

class Animal
{
    public Animal(string name){this.name = name;}
    public string name = "Animal";
    public virtual void Shout()
    {Console.WriteLine("Animal.Shout()");}
}
 class Dog : Animal
{
    public Dog(string name) : base(name){}
    public override void Shout()
    {
        Console.WriteLine("Dog 汪!");
    }
}
 class Cat : Animal
{
    public Cat(string name) : base(name){}
    public override void Shout()
    {
        Console.WriteLine("Cat 喵!");
    }
}

輸出結果為:

Dog!
Cat!
Animal.Shout()
Dog!

理解:

  • 同abstract,只是父類Animal不再是抽象類,因此Animal類可以例項化。

多次override,例子一

using System;

public class Test
{   
    public static void Main()
    {
        Animal dog = new Dog("dog");
        Animal cat = new Cat("cat");
        dog.Shout();
        cat.Shout();    
        Animal bigDog = new BigDog("BigDog");
        bigDog.Shout();
    }
}

class Animal
{
    public Animal(string name){this.name = name;}
    public string name = "Animal";
    public virtual void Shout()
    {Console.WriteLine("Animal.Shout()");}
}
 class Dog : Animal
{
    public Dog(string name) : base(name){}
    public override void Shout()
    {
        Console.WriteLine("Dog 汪!");
    }
}
 class Cat : Animal
{
    public Cat(string name) : base(name){}
    public override void Shout()
    {
        Console.WriteLine("Cat 喵!");
    }
}
class BigDog : Dog
{
    public BigDog(string name) : base(name){}
    public override void Shout()
    {
        Console.WriteLine("BigDog 汪!");
    }
}

輸出

Dog!
Cat!
BigDog!

理解

  • 同abstract,因此Animal bigDog = new BigDog(“BigDog”); bigDog.Shout();呼叫的是BigDog類中的Shout()方法。
  • 如果子類中的該方法被override,則子類的子類中的該方法不能被隱藏,要麼被override重寫,要麼不寫。

多次override例子二

using System;

public class Test
{   
    public static void Main()
    {
        Animal dog = new BigBigDog("BigBigDog");
        dog.Shout();
        Animal bigdog = new BigDog("bigdog");
        bigdog.Shout();

        Console.WriteLine("----------");

        BigDog dog2 = new BigBigDog("BigBigDog");
        dog2.Shout();
        BigDog bigdog2 = new BigDog("bigdog");
        bigdog2.Shout();
    }
}

class Animal
{
    public Animal(string name){this.name = name;}
    public string name = "Animal";
    public virtual void Shout()
    {Console.WriteLine("Animal.Shout()");}
}
 class Dog : Animal
{
    public Dog(string name) : base(name){}
    public override void Shout()
    {
        Console.WriteLine("Dog 汪!");
    }
}
class BigDog : Dog
{
    public BigDog(string name) : base(name){}

}
class BigBigDog : BigDog
{
    public BigBigDog(string name) : base(name){}
}

輸出

Dog 汪!
Dog 汪!
----------
Dog 汪!
Dog 汪!

理解

  • BigBigDog沒有Shout方法,向父類一層層找,找到基類Animal的Shout方法,發現是virtual虛方法,再向上找override重寫後的方法所在的類(直到BigBigDog這層為止),結果找到了Dog類中被重寫的Shout,並且再向上找Dog類與BigBigDog之間,沒有對應的重寫後的方法,於是最終呼叫的是Dog.Shout()方法。
  • BigDog dog2 = new BigBigDog(“BigBigDog”);實際上dog2所在類BigBigDog中沒有Shout方法,向父類找到基類Animal的Shout方法,其他同上。
  • BigDog bigdog2 = new BigDog(“bigdog”);同上。

多型

using System;

public class Test
{   
    public static void Main()
    {
        Animal dog = new Dog("dog");
        Animal cat = new Cat("cat");
        MyShout(dog);
        MyShout(cat);


    }
    static void MyShout(Animal animal)
    {
        animal.Shout();
    }
}

abstract class Animal
{
    public Animal(string name){this.name = name;}
    public string name = "Animal";
    public abstract void Shout();

}
 class Dog : Animal
{
    public Dog(string name) : base(name){}
    public override void Shout()
    {
        Console.WriteLine("Dog 汪!");
    }
}
 class Cat : Animal
{
    public Cat(string name) : base(name){}
    public override void Shout()
    {
        Console.WriteLine("Cat 喵!");
    }
}

理解

  • 與abstract例子一很類似,MyShout方法只知道引數是Animal,並不知道具體被傳入的是什麼動物(貓還是狗),但是卻能夠運作良好,傳入狗和貓時分別呼叫了狗和貓的Shout方法。

補充

  • 應該避免從實體類Animal繼承,而是將Animal定義為抽象類並繼承,因為例項化Animal是無意義的(不知道具體是什麼動物所以不知道怎麼Shout),我們只需要利用多型特性。
  • 如果Animal是抽象類,那麼就可以避免傳入Animal類的例項(因為抽象類不可以被例項化,想建立一個Animal類例項作為MyShout的引數根本無法通過編譯)。

思考一:想想以下程式碼的輸出是什麼?

using System;

public class Test
{
    public static void Main()
    {
        var b = new B(12);
        var a = b as A;
        Console.WriteLine("---------------");
        Console.WriteLine("a.GetA={0},a.GetAA={1}  b.GetA()={2},b.GetAA()={3}", a.GetA(), a.GetAA(), b.GetA(), b.GetAA());
        Console.WriteLine("---------------");
        var a1 = new A(10);
        Console.WriteLine("a1.GetA()={0}", a1.GetA());
    }
}

public class A
{
    protected int m_a;
    public A(int _a) { m_a = _a; Console.WriteLine("constructor A, m_a={0}", m_a);}
    public virtual int GetA()
    {
        Console.WriteLine("call a.GetA, return this.m_a={0}", m_a);
        return m_a;
    }
    public int GetAA()
    {
        Console.WriteLine("call a.GetAA, return this.m_a={0}", m_a);
        return m_a;
    }
}
public class B : A
{
    //思考二:將下一行程式碼註釋後,輸出是什麼
    private int m_a;
    public B(int _a) : base(_a) { m_a+=_a-1; Console.WriteLine("constructor B, private b.m_a={0}", m_a);}
    public override int GetA()
    {
        Console.WriteLine("call b.GetA, return this.m_a={0}", m_a);
        return m_a;
    }
}

參考連結:
https://www.cnblogs.com/joeylee/p/3631246.html