抽象类和接口这两种类型用于完全不同的目的。抽象类主要用作对象的基类,贡献某些主要的特性,例如共同的目的和结构。
接口则主要是用于类,为这些类去添加不同的方法(行为)。
抽象类和接口都包含可以由派生类继承的成员,他们都能直接实例化,但可以声明他们的变量;
这样做,可以使用多态性把继承这两种类型的对象指定给他们的变量;
接着通过这些变量来使用这些类型的成员,但不能直接访问派生类中的其他成员【如上述例子】
1、他们的派生类只能继承一个基类,即只能直接继承一个抽象类,但可以继承任意多个接口;
2、抽象类中可以定义成员的实现,但接口中不可以;
3、抽象类中可以包含字段、构造函数、析构函数、静态成员或常量等,接口中不可以;
4、抽象类中的成员可以是私有的(只要他们不是抽象的)、受保护的、内部的或受保护的内部成员(受保护的内部成员只能在应用程序的代码或派生类中访问),但接口中的成员必须是公共的。
一般将父类定义为抽象类,需要使用这个父类进行多态与继承。
继承树中越是向上,类就越发抽象;例如:奔驰继承汽车类,汽车类继承交通工具类等。
声明:使用abstract关键字
访问修饰符 abstract class 类名:基类或接口
{
}
声明抽象类
public abstract class myClass
{
public int i;
public void func()
{ }
}
声明抽象类时,abstract、class和类名外是必须的;其余可选
抽象方法没有方法体,这个方法本身没有任何意义,需要被继承的类进行重写。
如果一个类中有抽象方法,那么这个类就需要被标记为抽象类。
抽象方法必须声明在抽象类中。
声明抽象方法时,不能使用virtual、static、private修饰符。
抽象方法声明引入了一个新方法,不提供方法的实现,因此方法体只包含一个分号。
当从抽象类的派生一个非抽象类时,需要在非抽象类中重写抽象方法,以提供具体的实现,重写抽象方法时使用override关键字。
声明:使用使用abstract关键字
public abstract class myClass
{
public int i;
public void func()
{ }
//声明抽象方法
public abstract void method();
}
//声明抽象类
abstract class MyClass
{
private string name;
private int age;
public string Name
{
get { return name; }
set { name = value; }
}
public int Age
{
get { return age; }
set { age = value; }
}
//抽象方法
public abstract void showInfo();
}
//继承抽象类
class SonClass : MyClass
{
//重写抽象方法
public override void showInfo()
{
Console.WriteLine($"My name is:{this.Name},I am {this.Age} years old.");
}
}
internal class Program
{
static void Main(string[] args)
{
SonClass sonClass = new SonClass(); //实例化派生类
MyClass son = sonClass; //使用派生类实例化抽象类
son.Age = 10;
son.Name = "Nick";
son.showInfo();
Console.ReadLine();
}
}
由于C#中是单继承,但客观世界中出现多重继承的情况比较多,因此通过接口实现多重继承的功能。
接口是抽象类的延申,可以视作纯粹的抽象类,其中的所有方法都没有方法体。
接口用来定义程序的协议,描述可属于任何类或结构的一组相关行为。
接口可由 方法、属性、事件、索引器 4种成员类型的任何组合构成,但不能包含字段。
类和结构可以继承多个接口,接口也可以继承其他的接口。
接口可以将方法、属性、索引器、事件作为成员,但不能给这些成员设置具体的值。
特征:
类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员。
不能直接实例化接口。
接口可以包含事件、索引器、方法和属性。
接口不包含方法的实现。
类和结构可从多个接口继承。
接口自身可从多个接口继承。
使用interface关键字,语法格式如下:
修饰符 interface 接口名称:继承的接口列表
{
接口内容;
}
声明接口时,除interface关键字和接口名称外,其他的都是可选项;
可使用new、public、protected、internal、private等修饰符声明接口,但接口成员必须是公开的。
interface ImyInterface
{
string ID //编号(可读可写)
{
get;
set;
}
string Name //姓名(可读可写)
{
get;
set;
}
void ShowInfo(); //显式定义的编号和姓名
}
实现接口成员,类中对应的成员必须是公共的、非静态的,并且与接口成员具有相同的名称和签名。
类的属性和索引器可以为接口上定义的属性或索引器定义额外的访问器;例如,接口定义一个带有get的访问器属性,而实现该接口的类,可以声明同时带有get和set访问器的同意属性;但是,如果属性或索引器使用 显示 实现,则访问器必须匹配。
接口的实现通过类继承实现。声明实现接口的类时,需要在基类列表中包含所实现的接口的名称。
interface ITest1
{
void Test1();
}
interface ITest2:ITest1
{
void Test2();
}
class TestClass : ITest2
{
public void Test1()
{
Console.WriteLine("ITest1接口的Test1()方法");
}
public void Test2()
{
Console.WriteLine("ITest2接口的Test2()方法");
}
}
如果类实现两个接口,并且两个接口包含具有相同签名的成员,那么在类中实现该成员将导致两个接口都使用该成员作为他们的实现。
这样可能会导致接口实现的不正确,因此需要显示实现接口成员,即创建一个仅通过该接口调用并且特定于该接口的类成员。
显示接口实现是通过 使用 接口名称和一个句点 命名该类成员来进行实现。
显示接口成员实现中 不能包含 访问修饰符、abstract、virtual、override或static修饰符。
显示接口成员属于接口的成员,而不是类的成员你,因此,不能使用类对象直接访问,只能通过接口对象来访问。
interface ImyInterface1
{
int Add();
}
interface ImyInterface2
{
int Add();
}
class My_Class : ImyInterface1, ImyInterface2
{
int ImyInterface2.Add() { return 1; }
int ImyInterface1.Add() { return 0; }
}
internal class Program
{
static void Main(string[] args)
{
My_Class myClass = new My_Class();
//myClass对象中不包含Add方法,只能通过实例化接口对象来调用Add方法
ImyInterface1 imyInterface1 = myClass;
Console.WriteLine($"调用接口ImyInterface1 中的Add方法结果为: {imyInterface1.Add()}");
//将myClass作为ImyInterface2,来调用其中的Add()方法
Console.WriteLine($"调用接口ImyInterface2 中的Add方法结果为: {(myClass as ImyInterface2).Add()}");
Console.ReadLine();
}
}