重構
class Animal{
//...
public string Shout(){//去除virtual,成爲普通的公共方法
string result = "";
for(int i=0;i<shoutNum;i++)
result += getShoutSound()+", ";//調用一個虛方法
return "我的名字叫"+name+" "+result;
}
protected virtual string getShoutSound(){
//"得到叫聲",虛方法,讓子類重寫,只需給繼承的子類使用,所以用protected修飾符
return "";
}
}
class Cat:Animal{
public Cat():base(){}
public Cat(string name):base(name){}
public override string getShoutSound(){
return "喵";
}
}
class Dog:Animal{
public Dog():base(){}
public Dog(string name):base(name){}
public override string getShoutSound(){
return "汪";
}
}
class Cattle:Animal{
public Cattle():base(){}
public Cattle():base(name){}
public override string getShoutSound(){
return "哞";
}
}
抽象類
C#允許把類和方法聲明爲abstract,即抽象類和抽象方法。
注意:1、抽象類不能實例化;2、抽象方法是必須被子類重寫的方法;3、如果類中包含抽象方法,那麼類就必須定義爲抽象類,不論是否還包含其他一般方法。
abstract class Animal{
//...
protected abstract string getShoutSound();
}
抽象類擁有儘可能多的共同代碼,擁有儘可能少的數據。抽象類通常代表一個抽象概念,它提供一個繼承的出發點,當設計一個新的抽象類時,一定是用來繼承的,所以,在一個以繼承關係形成的等級結構裏面,樹葉節點應當是具體類,而樹枝節點均應當是抽象類。
接口
接口是把隱式公共方法和屬性組合起來,以封裝特定功能的一個集合。一旦實現了接口,類就可以支持接口所指定的所有屬性和成員。聲明接口在語法上與聲明抽象類完全相同,但不允許提供接口中任何成員的執行方式。故接口不能實例化,不能有構造方法和字段,不能有修飾符,不能聲明虛擬的或靜態的等。還有實現接口的類就必須要實現接口中的所有方法和屬性。一個類可以支持多個接口,多個類也可以支持相同的接口。接口的命名,前面要加一個大寫的‘I’
interface IChange{
//聲明一個IChange接口,此接口有一個方法ChangeThing,參數爲字符串,返回字符串
string ChangeThing(string thing);
}
class MachineCat:Cat,IChange{
//機器貓繼承於貓,並實現IChange接口
public MachineCat():base(){}
public MachineCat(string name):base(name){}
public string ChangeThing(string thing){//實現接口的方法,注意不能加override修飾符
//base.Shout()表示調用父類Cat的方法
return base.Shout()+"我有萬能的口袋,我可變出:"+thing;
}
}
private void button6_Click(object sender,EventArgs e){
MachineCat mcat = new MachineCat("叮噹");
StoneMonkey wukong = new StoneMonkey("孫悟空");
IChange[] array = new IChange[2];
array[0] = mcat;
array[1] = wukong;
//利用多態性,實現不同的ChangeThing
MessageBox.Show(array[0].ChangeThing("各種各樣的東西"));
MessageBox.Show(array[1].ChangeThing("各種各樣的東西");
}
抽象類和接口的區別
抽象類可以給出一些成員的實現,接口卻不包含成員的實現,抽象類的抽象成員可被子類部分實現,接口的成員需要實現類完全實現,一個類只能繼承一個抽象類,但是可以實現多個接口。
類是對對象的抽象;抽象類是對類的抽象;接口是對行爲的抽象。如果行爲跨越不同類的對象,可以使用接口;對於一些相似的類對象,用繼承抽象類。從設計角度講,抽象類是從子類中發現了公共的東西,泛化出父類,然後子類繼承父類,而接口是根本不知子類的存在,方法如何實現還不確認,預先定義。
集合
數組優點,數組在內存中連續存儲,因此可以快速而容易地從頭到尾遍歷元素,可以快速修改元素等等。數組缺點,創建時必須要指定大小,相鄰元素之間 插入困難。
.NET Framework提供了用於數據存儲和檢索的專用類,稱集合。這些類提供對堆棧、隊列、列表和哈希表的支持。大多數集合類實現相同的接口。
using System.Collections;
public partial class Form1: Form{
IList arrayAnimal;
//動物報名按鈕事件
private void button3_Click(object sender,EventArgs e){
arrayAnimal = new ArrayList();//實例化ArrayList對象
arrayAnimal.Add(new Cat("小花"));
arrayAnimal.Add(new Dog("阿毛"));
arrayAnimal.Add(new Dog("小黑"));
arrayAnimal.Add(new Cat("嬌嬌"));
arrayAnimal.Add(new Cat("咪咪"));
MessageBox.Show(arrayAnimal.Count.ToString());
}
}
private void button4_Click(object sender,EventArgs e){
foreach(Animal item in arrayAnimal){
MessageBox.Show(item.Shout());
}
}
注意元素移除後,爲保持元素連續,原來元素索引改變。
ArrayList不是類型安全的,在其眼中元素都是Object。用ArrayList就意味着都需要將值類型裝箱爲Object對象,使用元素時,還需要執行拆箱操作,帶來很大的性能損耗。
//所謂裝箱就是把值類型打包到Object引用類型的一個實例中
int i = 123;
object o = (object) i;
//拆箱就是從對象中提取值類型
o = 123;
i = (int) o;
泛型
泛型是具有佔位符(類型參數)的類、結構、接口和方法,這些佔位符是類、結構、接口和方法所存儲或使用的一個或多個類型的佔位符。泛型集合類可以將類型參數用作它所存儲的對象的類型的佔位符;類型參數作爲其字段的類型和其方法的參數類型出現。
using System.Collections.Generic;
public partial class Form1:Form{
IList<Animal> arrayAnimal;//聲明一個泛型集合變量,用接口IList,表示只接受Animal類型
private void button3_Click(object sender,EventArgs e){
arrayAnimal = new List<Animal>();
arrayAnimal.Add(new Cat("小花"));
arrayAnimal.Add(new Dog("阿毛"));
arrayAnimal.Add(new Dog("小黑"));
arrayAnimal.Add(new Cat("嬌嬌"));
arrayAnimal.Add(new Cat("咪咪"));
MessageBox.Show(arrayAnimal.Count.ToString());
}
}
通常情況下,都建議使用泛型集合,因爲這樣可以獲得類型安全的直接優點而不是需要從基集合類型派生並實現類型特定的成員。此外,如果集合元素爲值類型,泛型集合類型的性能通常優於對應的非泛型集合類型,因爲使用泛型時不必對元素進行裝箱。
委託與事件
委託是對函數的封裝,可以當做給方法的特徵指定一個名稱。而事件則是委託的一種特殊形式,當發生有意義的事情時,事件對象處理通知過程。
委託是一種引用方法的類型。一旦爲委託分配了方法,委託將與該方法具有完全相同的行爲。委託對象用關鍵字delegate;來聲明。而事件是說發生在其他類或對象關注的事情時。類或對象可通過事件通知他們。事件用event關鍵字聲明。
class Cat{
private string name;
public Cat(string name){
this.name = name;
}
//聲明委託
public delegate void CatShoutEventHandler();
//聲明事件,他的事件類型是委託CatShoutEventHandler
public event CatShoutEventHandler CatShout;
public void Shout(){
Console.WriteLine("喵,我是{0}。",name);
if(CatShout!=null){//如果CatShout中有對象登記事件則執行CatShout()
CatShout();
}
}
}
class Mouse{
private string name;
public Mouse(string name){
this.name = name;
}
public void Run(){
Console.WriteLine("老貓來了,{0}快跑!",name);
}
}
static void Main(string[] args){
Cat cat = new Cat("Tom");
Mouse mouse1 = new Mouse("Jerry");
Mouse mouse2 = new Mouse("Jack");
//將Mouse的Run 方法通過實例化委託Cat.CatShoutEventHandler登記到Cat的事件CatShout中
cat.CatShout+ =new Cat.CatShoutEventHandler(mouse1.Run);
cat.CatShout+ =new Cat.CatShoutEventHandler(mouse2.Run);
cat.Shout();
Console.Read();
}
EventArgs是包含事件數據的類的基類
public class CatShoutEventArgs:EventArgs{//繼承EventArgs
private string name;
public string Name{
get{return name;}
set{name=value;}
}
}
class Cat{
private string name;
public Cat(string name){
this.name = name;
}
//聲明委託,含參數
public delegate void CatShoutEventHandler(object sender,CatShoutEventArgs args);
//聲明事件,他的事件類型是委託CatShoutEventHandler
public event CatShoutEventHandler CatShout;
public void Shout(){
Console.WriteLine("喵,我是{0}。",name);
if(CatShout!=null){//如果CatShout中有對象登記事件則執行CatShout()
CatShoutEventArgs e = new CatShoutEventArgs();
e.Name = this.name;
CatShout(this,e);
}
}
}
class Mouse{
private string name;
public Mouse(string name){
this.name = name;
}
public void Run(object sender,CatShoutEventArgs args){
Console.WriteLine("老貓{0}來了,{1}快跑!",args.name,name);
}
}