C#易错易混淆知识总结(八)--{接口}
一 接口
对于接口来说,C#是有规定使用Interface关键字来声明接口。它的声明是和类一致的。可以说接口就是一个特殊的抽象类。如下代码:
class Program { static void Main(string[] args) { } } //声明一个可以飞的接口 interface IRunable { //包含可以被继承的子类实现的方法 void Run(); }
由以前的抽象类的知识可以知道,抽象类是没有办法实例化的(因为含有抽象成员,而抽象成员不含有方法体)。那么接口可不可以实例化呢?答案是肯定的,不能实例化。
二,接口可以定义哪些成员
1)接口就是一个定义“具有某种能力的抽象类”,既然接口是类,那么它的内部可以定义哪些成员呢?
首先,在普通的类中,可以有字段,属性,方法,索引器,抽象方法等等。那么接口呢?
看下面直接声明字段,编译器会报错,告诉我们接口内不能声明字段
既然接口内不能有字段,那也就不存在封装字段了。所以上边图示的封装字段的代码也是错误的。
同理由上面的代码也可以知道,在接口中是不可以定义显式的属性(因为在属性中要操作字段赋值,但是字段没有办法在接口中声明)。
那么接口可以声明自动属性么?看下面的代码:
//声明一个可以飞的接口 interface IRunable { //声明字段 int nAge { get; set; } string strName { get; set; } ////包含可以被继承的子类实现的方法 void Run(); }
代码可以顺利编译通过,那么是为什么呢?这就要看.NET的源码,我把源码编译后的比较结果如下图:
抽象方法就不用多了,本来接口就是一个抽象爱类,当然可以定义抽象类,但是不在使用abstract关键字,而且方法必须没有方法体;
2)继承接口的子类必须实现接口的所有抽象成员。
可以发现:
①我们的继承类在实现接口成员的时候不需要使用override关键字
②实现接口的时候,必须保持签名一致
由前面抽象类的知识我们有没有这样的疑问,什么时候使用抽象类,什么时候使用接口呢?
总结如下:
①使用抽象类:可以找到父类,并且希望通过父类继承给子类一些成员
②使用接口:接口就是一个纯粹的为了规范实现的类。比如:多个类具有相同的方法,但是却找不到父类,就可以将方法定义在接口中。让这些类去实现。
下面纠纷别来看两端代码,比较抽象类和接口的异同,首先是抽象类:
class Program
{
static void Main(string[] args)
{
Student s = new Student();
//Student类通过继承获得NAge属性
s.NAge = 10;
s.Eat();
Console.WriteLine("--------Student和Worker类分别通过继承获得了父类的非私有成员,实现了父类的抽象方法--------");
Worker w = new Worker();
//Worker类通过继承获得NAge属性
w.NAge = 40;
w.Eat();
Console.ReadKey();
}
}
//定义父类
abstract class Person
{
private int nAge;
public int NAge
{
get { return nAge; }
set { nAge = value; }
}
private void Run()
{
Console.WriteLine("我是父类,我可以跑!");
}
public abstract void Eat();
}
class Student : Person
{
//子类覆写了父类的抽象方法
public override void Eat()
{
Console.WriteLine("我是子类,我继承了父类,我可以在学校吃饭!");
}
}
class Worker:Person
{
//同样Worker也通过继承获得了父类的非私有成员
public override void Eat()
{
Console.WriteLine("我是子类,我继承父类,我可以在工厂吃饭");
}
}
接下来,来看看接口是怎么规范多个类的实现的。
class Program
{
static void Main(string[] args)
{
Student s = new Student();
s.strName = "小学生";
s.Run();
Console.WriteLine(s.strName);
Console.WriteLine("--------------------");
Worker w = new Worker();
w.strName = "看我能不能渎职";
w.Run();
Console.WriteLine(w.strName);
Console.ReadKey();
}
}
interface IRunable
{
//规范子类必须实现strName属性
string strName { get; set; }
//规范子类必须实现Run()方法
void Run();
}
class Student:IRunable
{
//这里是子类的字段
string strname;
public string strName
{
get
{
return strname;
}
set
{
strname = value;
}
}
public void Run()
{
Console.WriteLine("我是小学生,我在学校里面跑步!");
}
}
class Worker:IRunable
{
string strname;
public string strName
{
get
{
return "工人";
}
set
{
strname = value;
}
}
public void Run()
{
Console.WriteLine( "我是工人,我需要在厂区跑!");
}
}
由以上的代码可不可以发现,接口仅仅在规定一个规范子类的实现,而抽象类可以通过继承,继承给子类某些成员。
最后来看一下,接口的显示实现,我先看接口的普通实现(以上的代码实现接口的方式都是隐式实现)
interface IRunable
{
//规范子类必须实现strName属性
string strName { get; set; }
//规范子类必须实现Run()方法
void Run();
}
class Student:IRunable
{
//这里是子类的字段
string strname;
public string strName
{
get
{
return strname;
}
set
{
strname = value;
}
}
public void Run()
{
Console.WriteLine("我是小学生,我在学校里面跑步!");
}
}
显式实现接口
class Student:IRunable
{
//这里是子类的字段
string strname;
//显示实现接口
string IRunable.strName
{
get
{
return strname;
}
set
{
strname = value;
}
}
void IRunable.Run()
{
Console.WriteLine("我是小学生,我在学校里面跑步!");
}
}
显示的实现接口是为了解决方法名冲突的问题。但是显示实现接口会出现,在上面的代码中会出现一个问题,如下图:
为什么会这样呢?
因为显式实现接口的方法是私有的,不能通过对象变量来调用。那应该怎么调用呢,看下面的代码:
class Program
{
static void Main(string[] args)
{
//里氏替换原则,父类变量指向子类对象,并通过父类变量调用子类方法
IRunable ir = new Student();
ir.Run();
Console.ReadKey();
}
}
interface IRunable
{
//规范子类必须实现strName属性
string strName { get; set; }
//规范子类必须实现Run()方法
void Run();
}
class Student:IRunable
{
//这里是子类的字段
string strname;
//显示实现接口
string IRunable.strName
{
get
{
return strname;
}
set
{
strname = value;
}
}
void IRunable.Run()
{
Console.WriteLine("我是小学生,我在学校里面跑步!");
}
// Student s = new Student();
}
打印结果如下:
显式实现接口,这个接口的方法,只能通过接口变量来调用。
总结:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律