7.1 抽象类
7.1.1 生活中的抽象类
前面已经学习过类,知道类是对对象的抽象,那么抽象类可以看作是对类的抽象。抽象类的用途是提供一个可供多个派生类共享的通用基类定义
7.1.2 抽象类的概念
在面向对象思想中,所有的对象都可以通过类来描述。但并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够能描述具体对象的信息,那么就称其为抽象类
语法:
abstract class 类名 { }
下面以电器充电的例子来说明抽象类
示例:
class Computer: Appliance
{
public override void Charge()
{
Console.WriteLine(" 电脑使用 220V 电压充电! ");
}
}
class MobilePhone:Appliance
{
public override void Charge()
{
Console.WriteLine(" 手机使用 110V 电压充电! ");
}
}
abstract class Appliance
{
public abstract void Charge();
}
static void Main(string[] args)
{
Appliance cpt = new Computer();//父类引用指向子类对象
Appliance mp = new MobilePhone();
cpt.Charge();
mp.Charge();
}
在 C# 中使用抽象类要注意以下几个要点
- 抽象类可以不包含抽象方法,但是含有抽象方法的类必须定义为抽象类
- 抽象方法不允许有方法体
- 子类继承于抽象类时必须重写抽象类中所有的抽象方法,除非子类也是抽象类
- 抽象类不允许直接实例化,只能实例化其非抽象子类对象
7.1.3 抽象方法和虚方法的区别
抽象方法和虚方法都是为了实现多态功能,其主要区别
- 抽象方法只提供一个功能名称,要求子类必须实现该功能
- 虚方法不只提供功能名称还提供了默认实现方式,子类可以不重新实现该功能
抽象方法:
- 使用 abstract 关键字修饰
- 只能定义在抽象类中
- 不允许有方法体
- 子类必须重写
虚方法:
- 使用 virtual 关键字修饰
- 可以定义在除密封类以外的类中
- 必须有方法体
- 子类可以重写
7.2 接口
7.2.1 生活中的抽象类
- 日常生活中比较常见的接口有电源接口、电脑上的 USB 接口等
- 在电源接口上接不同的电器可以使用不同的功能,比如接上冰箱就可以冷冻食物,接上电视机就可以观看电视节目,接上洗衣机就可以洗衣服
- 在电脑上也可以连接各种带 USB 接口的设备,比如连接摄像头、U 盘和鼠标等
7.2.2 接口的概念
C# 中子类最多只允许有一个父类。如果一个类需要具有多个类的特征或行为,那么可以通过接口来实现。C# 中的接口用于约束类的行为
class Soldier
{
public void Training()
{
Console.WriteLine(" 开始训练! ");
}
}
class Actor
{
public void Performance()
{
Console.WriteLine(" 开始表演! ");
}
}
class CivilianSoldier : Soldier, Actor//违反继承规则
{
}
修改上述示例,使用接口实现
//定义士兵类和表演接口
class Soldier
{
public void Training()
{
Console.WriteLine(" 开始训练! ");
}
}
interface IPerformance
{
void Show();
}
//继承类,并实现接口
class CivilianSoldier : Soldier, IPerformance
{
public void Show()
{
Console.WriteLine(" 文艺兵在表演舞蹈! ");
}
}
static void Main(string[] args)
{
//创建类的对象,调用继承的方法和通过接口实现的方法
CivilianSoldier cs = new CivilianSoldier();
cs.Training();
cs.Show();
}
7.2.3 C#中的接口
语法:
[ 访问修饰符 ] interface 接口名 { }
注意:
- 接口名一般使用大写字母“I”开头,表明它是接口类型
- 接口可包含方法、属性、事件和索引器,但不能包含字段
- 接口中定义的成员不允许有具体实现
- 接口中的成员不允许加任何访问修饰符
7.2.1 生活中的抽象类
.NET 平台提供了非常多的接口
7.2.2 接口的概念
已知集合可提供排序方法对集合中的对象进行排序,但在集合中使用自定义类时则会出现类型未经处理的错误
class Person
{
private int age;
public int Age
{
get { return age; }
set { age = value; }
}
//其他属性及方法省略…
}
static void Main(string[] args)
{
List<Person> list = new List<Person>();
//初始化集合及循环显示代码省略…
list.Sort();//调用集合中自带的排序方法
}
实现平台提供的 IComparable 接口,即可进行排序
class Person: IComparable<Person>//实现平台自带接口
{
// 其他代码略
public int CompareTo(Person other)
{
return this.Age.CompareTo(other.Age);
}
}
7.3 抽象类和接口的区别
7.3.1 语法的区别
在C# 中抽象类和接口在使用上有很多相同点,如两者都不能实例化,都可以包含未实现的方法,其子类必须实现其未实现的方法等,但是在定义时却有很大的区别
抽象类 |
接口 |
使用 abstract 关键字 |
使用 interface 关键字 |
可以包含字段和已实现成员 |
不能有字段,只能包含未实现的成员 |
子类通过 override 实现抽象方法 |
子类直接实现所有成员 |
- 抽象类是一种特殊的类,不能被实例化,除此之外可以像普通类一样使用
- 抽象类的用场合侧重于有继承关系的一组对象
- 而接口用于定义一组行为规则,一般用于多个不同种类对象具有相同行为或功能的场合
7.3.2 应用场合的区别
抽象类使用场合示例
//定义抽象类学生类
abstract class Student
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
public abstract void Sport();
}
//定义小学生类,继承学生类,实现抽象方法
class Pupil : Student
{
public override void Sport()
{
Console.WriteLine(" 小学生参加短跑 200 米比赛! ");
}
}
//定义大学生类,继承学生类,实现抽象方法
class Undergraduate : Student
{
public override void Sport()
{
Console.WriteLine(" 大学生参加足球比赛! ");
}
}
//定义研究生类,继承学生类,实现抽象方法
class Postgraduate : Student
{
public override void Sport()
{
Console.WriteLine(" 研究生参加篮球比赛! ");
}
}
static void Main(string[] args)
{
Pupil stuOne = new Pupil(); //创建小学生、大学生、研究生对象
Undergraduate stuTwo = new Undergraduate();
Postgraduate stuThree = new Postgraduate();
List<Student> list = new List<Student>();//定义学生类的集合,可以保存学生类的子类
list.Add(stuOne);
list.Add(stuTwo);
list.Add(stuThree);
foreach (Student stu in list)
{
stu.Sport();
}
}
接口使用场合示例
//定义推销员类和护士类,分别实现唱歌接口
class Salesman : ISing
{
public void Sing()
{
Console.WriteLine(" 推销员开始演唱《映山红》。");
}
}
class Nurse : ISing
{
public void Sing()
{
Console.WriteLine(" 护士开始演唱《向天再借五百年》。");
}
}
static void Main(string[] args)
{
Salesman salesman = new Salesman();
Nurse nurse = new Nurse();
List<ISing> list = new List<ISing>();//定义集合,可以存储所有实现唱歌功能的对象
list.Add(salesman);
list.Add(nurse);
foreach (ISing item in list)
{
item.Sing();
}
}
//定义唱歌接口
interface ISing
{
void Sing();
}
总结
- 抽象方法只提供一个功能名称,要求子类必须实现该功能
- 而虚方法不只提供功能名称还提供了默认实现方式,子类可以不重新实现该功能
- 抽象类使用 abstract 关键字,可以包含字段和已实现成员,子类通过 override 实现抽象方法
- 接口使用 interface 关键字,不能有字段,只能包含未实现的成员,子类直接实现所有成员