virtual、abstract、override與多型
阿新 • • 發佈:2018-11-24
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方法,所以很好理解。
其他補充:
- 抽象類可以有建構函式,雖然抽象類不可以例項化,但是可用於為其派生類在例項化之前進行一些公共的初始化操作(賦值欄位、執行一些公共邏輯等)
- 在本例中,需要為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;
}
}