[https://blog.csdn.net/qq_42672770/article/details/107730430]
1.抽象类与接口的概念
类是具有相同或相似结构、操作和约束规格的对象组成的结合。而对象是某一类的具体实例化。每一个类都是具有某些共同特征的对象的抽象。
如下图,我们可以将所有的图形归为图形类,图形类即为一个抽象类(父类),但是对于具体的图形是三角形、四边形或者其他图形,无法准确描述。但是三角形、四边形等又都是继承于图形类的子类。具体是哪一类四边形又可分为正方形、平行四边形等
在多态机制中并不需要将父类初始化为对象,需要的是子类对象,图形类(父类)不能抽象出任何一种图形,但子类却可以。
继承抽象类的所有子类需要将抽象类中的抽象方法进行覆盖。但是类不能继承多个父类,即正方形不能同时继承图形类与四边形类,这是便产生了接口的概念。简单说,可以通过接口实现多重继承。
接口是对抽象类更为细致的划分,一个接口最好只能实现一个功能。抽象类是给同行使用的,接口是给外行使用的
1.1 抽象类与抽象方法
抽象类声明:
访问修饰符 abstract class 类名:基类或接口
例子:
public abstract class myclass
{
}
抽象方法:
public abstract class myclass
{
public abstract string method(); //抽象方法
}
抽象类与抽象方法注意点:
1.抽象方法只能声明在抽象类中
2.声明抽象方法不能使用virtual、static、private修饰符。如果添加程序会自动报错
3.抽象方法没有方法体,这个方法本身没有任何意义,除非它被重写,但承载这个抽象方法的抽象类必须被继承,实际上,抽象类除了被继承之外,没有任何意义。
4.只要类中有一个抽象方法,该类被标记为抽象类
5.当从抽象类派生一个非抽象类时,即继承于父类的子类,是可以被实例化的类。此时需要在非抽象类中重写抽象方法,以提供具体的实现,重写抽象方法时使用关键字override
1.2 抽象类与抽象方法使用实例
public abstract class myClass
{
private string id = "";
private string name = "";
/// <summary>
/// 编号属性及实现
/// </summary>
public string ID
{
get
{
return id;
}
set
{
id = value;
}
}
/// <summary>
/// 姓名属性及实现
/// </summary>
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
/// <summary>
/// 抽象方法,用来输出信息
/// </summary>
public abstract void ShowInfo();
}
public class DriveClass : myClass //继承抽象类
{
/// <summary>
/// 重写抽象类中输出信息的方法
/// </summary>
public override void ShowInfo()
{
Console.WriteLine(ID + " " + Name);
}
}
class Program
{
static void Main(string[] args)
{
DriveClass driveclass = new DriveClass(); //实例化派生类
myClass myclass = driveclass; //使用派生类对象实例化抽象类
myclass.ID = "BH0001"; //使用抽象类对象访问抽象类中的编号属性
myclass.Name = "TM"; //使用抽象类对象访问抽象类中的姓名属性
myclass.ShowInfo(); //使用抽象类对象调用派生类中的方法
Console.Read();
}
}
1.3 接口
接口是抽象类的延伸,可以将它看作是纯粹的抽象类。其定义方法如下:
修饰符 interface 接口名称 :继承的接口列表
{
接口内容;
}
如何理解接口是抽象类的延伸这句话呢?简单说,抽象类A中有3种抽象方法:A1方法、A2方法、A3方法,继承于抽象类A的有2个类:类B、类C,子类类B、类C是需要重写抽象类A的3种方法,类B中恰巧3种方法都需要,可在类B中重写A1、A2、A3方法,但类C中只需要抽象方法A1,重写A1的同时,也要重写A2、A3。这样程序中就会有太多的冗余代码,同时抽象类A的局限性也很大,不需要A2、A3方法的类C也必须重写A2、A3方法。这时,我们可以再定义抽象类D,抽象类A中只包含A1方法,抽象类D中包含A2、A3方法,子类类C可继承抽象A,子类类B需要继承抽象类A和抽象类D,但是一个子类是不能同时继承多个父类,接口便能很好的实现这种多重继承。
一个类虽然只能继承一个基类,但是可以继承任意接口。
接口常见的特征:
1.接口用于描述一组类的公共方法/公共属性
2.接口中的方法没有具体实现,也就是没有方法体,必须由继承者去实现而且必须全部实现。
3. 接口中的方法不需要修饰符,默认就是公有的(Default / Public)
4.接口可以包含方法、属性、索引器、事件。不能包含任何其他的成员,例如:常量、字段、域、构造函数、析构函数、静态成员
1.4 接口使用实例
1.4.1 接口单重继承
以下声明一个接口,接口中包含编号、姓名2个属性,包含一个自定义方法,代码如下:各部分注释已经再代码中体现
interface ImyInterface
{
/// <summary>
/// 编号(可读可写)
/// </summary>
string ID //接口中属性成员是公共的,不需要修饰符
{
get;
set;
}
/// <summary>
/// 姓名(可读可写)
/// </summary>
string Name
{
get;
set;
}
/// <summary>
/// 显示定义的编号和姓名
/// </summary>
void ShowInfo(); //接口中成员是公共的,不需要修饰符
}
class Program : ImyInterface//继承自接口
{
string id = ""; //字段
string name = "";
/// <summary>
/// 编号
/// </summary>
public string ID
{
get
{
return id;
}
set
{
id = value;
}
}
/// <summary>
/// 姓名
/// </summary>
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
/// <summary>
/// 显示定义的编号和姓名
/// </summary>
public void ShowInfo() //重写ShowInfo方法
{
Console.WriteLine("编号\t 姓名");
Console.WriteLine(ID + "\t " + Name);
}
static void Main(string[] args)
{
Program program = new Program(); //实例化Program类对象
ImyInterface imyinterface = program; //使用派生类对象实例化接口ImyInterface
imyinterface.ID = "TM"; //为派生类中的ID属性赋值
imyinterface.Name = "C#从入门到精通"; //为派生类中的Name属性赋值
imyinterface.ShowInfo(); //调用派生类中方法显示定义的属性值
Console.Read();
}
}
案例分析:继承自接口类class Program,继承了接口ImyInterface的属性和方法,属性定义为公有的,方法重新改写了,主程序中,先实例化类Program,再实例化接口将其与实例化的类绑定,imyinterface.ID、imyinterface.Name、imyinterface.ShowInfo()实际上是实例化类中的参数与方法,类似于委托。
1.4.2 接口多重继承
搞清楚了单重继承的关系,再看多重继承
interface IPeople
{
/// <summary>
/// 姓名
/// </summary>
string Name
{
get;
set;
}
/// <summary>
/// 性别
/// </summary>
string Sex
{
get;
set;
}
}
interface ITeacher : IPeople //继承公共接口
{
/// <summary>
/// 教学方法
/// </summary>
void teach();
}
interface IStudent : IPeople //继承公共接口
{
/// <summary>
/// 学习方法
/// </summary>
void study();
}
class Program : IPeople, ITeacher, IStudent//多接口继承
{
string name = "";
string sex = "";
/// <summary>
/// 姓名
/// </summary>
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
/// <summary>
/// 性别
/// </summary>
public string Sex
{
get
{
return sex;
}
set
{
sex = value;
}
}
/// <summary>
/// 教学方法
/// </summary>
public void teach()
{
Console.WriteLine(Name + " " + Sex + " 教师");
}
/// <summary>
/// 学习方法
/// </summary>
public void study()
{
Console.WriteLine(Name + " " + Sex + " 学生");
}
static void Main(string[] args)
{
Program program = new Program(); //实例化类对象
ITeacher iteacher = program; //使用派生类对象实例化接口ITeacher
iteacher.Name = "TM";
iteacher.Sex = "男";
iteacher.teach();
IStudent istudent = program; //使用派生类对象实例化接口IStudent
istudent.Name = "C#";
istudent.Sex = "男";
istudent.study();
Console.Read();
}
}
案例分析:多接口继承类Program ,继承了3个接口的属性、方法,并重新写了方法,主函数中的操作于接口中相同。
1.5 抽象类于接口使用情况
1.如果对象存在多个功能相近且关系紧密的版本,则使用抽象类。
2.如果对象关系不密切,但是若干功能拥有共同的声明,则使用接口。
3.抽象类适合于提供丰富功能的场合,接口则更倾向于提供单一的一组功能。