重构
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);
}
}