c#:浅析接口(interface)
c#接口是一个让很多c#初学者容易迷糊的东西,但是它有什么用处呢?如何使用呢?来让我们一起来谈谈究竟吧!
1、什么是接口
(1)接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 “是什么” 部分,派生类定义了语法合同 “怎么做” 部分。其实,接口简单理解就是一种约定,使得实现接口的类或结构在形式上保持一致。
①是什么部分:
接口只包含了成员的声明。也就是说接口只声明,类或结构一旦继承,那么就要按照结构声明的东西,重写接口中的所有数据,这就像是遵循了某种规则。
②怎么做部分:
成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。也就是说接口规定了这个规则,那么继承它的派生类(类/结构)就要按照接口制定的规则办事。
(2)接口使用interface关键字声明,并且接口的成员由属性、方法、事件、索引器。
(3)按照惯例,接口的名称必须以大写的 I 开头
2、接口的作用
在c#中,接口时独立于类来定义的。接口可以理解为一种约束,使得实现这个接口的类或者结构可以在形式上保持一致。也就是说接口可以看作为基类让类来继承约束类,并且还可以多继承,但是接口不能作为派生类继承其他的类。
3、接口如何声明
(1)接口的声明格式
/// <summary>
/// 定义一个工作的接口
/// </summary>
interface IWork
{
void Working();//接口成员不能有方法体
}
(2)接口的继承
/// <summary>
/// 定义一个工作的接口
/// </summary>
interface IWork
{
void Working();//接口成员没有实例方法,也就是没有方法体,需要派生类重写接口中的所有方法
}
/// <summary>
/// 定义一个人类,来继承接口
/// </summary>
public class Person : IWork
{
public void Working()//并实现接口的方法,也就是重写方法
{
Console.WriteLine("我是继承接口后的Person类");
}
}
//外部调用
class Program
{
static void Main(string[] args)
{
Person p = new Person();//实例化Person对象
p.Working();//调用person继承接口后拥有的重写方法working
Console.ReadKey();
}
}
4、接口的特征
(1)接口类似于被abstract关键字修饰的抽象基类,不能直接使用new关键字来实例化接口
(2)接口不能定义字段。接口中的方法都是抽象的或者说属性、方法、事件都没有实现部分,继承接口的任何非抽象类都必须实现(重写)接口的所有成员
(3)接口自身可以继承多个接口,类和结构也可以继承多个接口,但是接口不能继承类
/// <summary>
/// 定义一个工作的接口
/// </summary>
interface IWork
{
string Eat { get; set; }
void Working();
}
/// <summary>
/// 定义一个跑的接口
/// </summary>
interface IRun:IWork
{
void Run();
//因为接口之间可以相互继承,所以,IRun接口就继承了IWork接口中的成员方法IWorking
//此时当类或结构继承IRun接口时,不仅要重写IRun的接口,还要重写IWork的接口
}
/// <summary>
/// 定义一个人类,来继承接口
/// </summary>
public class Person : IRun
{
public string Eat { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public void Working()//并实现IWork接口的方法,也就是重写方法
{
Console.WriteLine("我是继承接口IRun后的Person方法Working");
}
public void Run()//重写IRun接口的方法
{
Console.WriteLine("我是继承接口IRun后的Person方法Run");
}
}
(4)接口不能包含静态成员、常量、字段、运算符,实例构造函数、析构函数或类型
(5)接口声明时可以使用public、internal(接口默认的)修饰符,但是接口成员都是自动公开的,不能使用任何访问修饰符,包括public
(6)当一个类既继承了基类,又继承接口的时候,必须基类写在前面,基类和接口用逗号隔开
**注意:**一个类只能有一个基类,但可以继承许多接口
例子:
/// <summary>
/// 接口:IWork
/// </summary>
public interface IWork
{
void Working();
}
/// <summary>
/// 接口:IRun
/// </summary>
public interface IRun
{
void Runt();
}
/// <summary>
/// 基类:人类
/// </summary>
public class Person
{
public void Sing()
{
Console.WriteLine("我会唱歌");
}
}
/// <summary>
/// 派生类:学生类
/// </summary>
class Student:Person,IWork,IRun//Student类继承Person(基类)、接口IWork、接口IRun
{
/// <summary>
/// 隐式接口:重写IWork接口中的Working方法
/// </summary>
public void Working()
{
Console.WriteLine("我是派生类Student,我继承并重写了接口IWork,我会工作");
}
/// <summary>
/// 隐式接口:重写IRu接口中的Runt方法
/// </summary>
public void Runt()
{
Console.WriteLine("我是派生类Student,我继承并重写了接口IRun,我会跑步");
}
}
//外部调用
class Program
{
static void Main(string[] args)
{
Student s = new Student();//实例化学生Student对象
s.Working();//这个是重写接口IWork中Working的方法
s.Runt();//这个是重写接口IRun中Runt的方法
s.Sing();//这个是继承Person类的方法
Console.ReadKey();
}
}
输出结果:
5、接口的分类
(1)显示实现接口
实现接口的过程中,若有两个接口中的某些成员有相同的名字则需用显示实现来解决,显示实现接口的类不能直接调用继承接口的成员。显示实现接口只能通过对应的接口访问对应接口中的方法,用实现类去访问时访问不到。其实很好理解,就是显示实现需要指定到具体的哪个接口的哪个方法,防止函数名冲突,调用模糊。
显式实现接口的特点:
1、显式实现的接口不能有访问修饰符
2、显式实现接口的方法可以看到方法的归属地([—某接口—].< 方法名 >),也就是说能够看到方法是从哪个接口而来的
3、显式实现接口的调用不能通过 类名.方法名 直接调用,必须通过多态,就是接口自己调用自己的方法或者属性,比如: 接口名称 P=new 类名称(); p.方法名();
例如:
/// <summary>
/// 接口:IWork
/// </summary>
public interface IWork
{
void Working();
}
/// <summary>
/// 接口:IRun
/// </summary>
public interface IRun
{
void Runt();
}
/// <summary>
/// 基类:人类
/// </summary>
public class Person
{
public void Sing()
{
Console.WriteLine("我会唱歌");
}
}
/// <summary>
/// 派生类:学生类
/// </summary>
class Student:Person,IWork,IRun//Student类继承Person(基类)、接口IWork、接口IRun
{
/// <summary>
/// 显式实现接口:重写IWork接口中的Working方法
/// </summary>
void IWork.Working()
//注意:显式实现接口没有访问修饰符,并且函数名是(对应接口.接口方法名)
{
Console.WriteLine("我是派生类Student,我继承并重写了接口IWork,我会工作");
}
/// <summary>
/// 显式实现接口:重写IRu接口中的Runt方法
/// </summary>
void IRun.Runt()
//注意:显式实现接口没有访问修饰符,并且函数名是(对应接口.接口方法名)
{
Console.WriteLine("我是派生类Student,我继承并重写了接口IRun,我会跑步");
}
}
//外部调用
class Program
{
static void Main(string[] args)
{
IWork s = new Student();//多态,接口IWork指向了对象Student
IRun r = new Student();//多态,接口Irun指向了对象Student
Student s1 = new Student();//这个是实例化Student类
//用对应接口调用对应的方法
s.Working();//接口s就是IWork调用它的方法Working
r.Runt();//接口R就是IRun调用它的方法Runt
s1.Sing();//通过派生类调用基类的方法
Console.ReadKey();
}
}
输出结果:
(2)隐式实现接口
隐式实现接口,当派生类继承一个接口的情况下就用隐式显示。这样就可以直接通过类名直接调用接口中的方法。
隐式实现接口的特点:
1、隐式实现的接口必须有访问修饰符public
2、隐式实现的接口不能看到方法的归属地,就是说不能看到方法来自哪个接口
3、隐式实现的接口可以直接通过 类名.方法名 来直接调用
例子:
/// <summary>
/// 接口:IWork
/// </summary>
public interface IWork
{
void Working();
}
/// <summary>
/// 接口:IRun
/// </summary>
public interface IRun
{
void Runt();
}
/// <summary>
/// 基类:人类
/// </summary>
public class Person
{
public void Sing()
{
Console.WriteLine("我会唱歌");
}
}
/// <summary>
/// 派生类:学生类
/// </summary>
class Student:Person,IWork,IRun//Student类继承Person(基类)、接口IWork、接口IRun
{
/// <summary>
/// 隐式实现接口:重写IWork接口中的Working方法
/// </summary>
public void Working()
{
Console.WriteLine("我是派生类Student,我继承并重写了接口IWork,我会工作");
}
/// <summary>
/// 隐式实现接口:重写IRu接口中的Runt方法
/// </summary>
public void Runt()
{
Console.WriteLine("我是派生类Student,我继承并重写了接口IRun,我会跑步");
}
}
//1、外部调用可以使用显示实现调用的方法(多态)
//2、同时隐式显示也可以使用类直接调用,这里介绍第二种
class Program
{
static void Main(string[] args)
{
Student s1 = new Student();//直接使用常见的类调用就可以
s1.Working();
s1.Runt();
s1.Sing();
Console.ReadKey();
}
}
输出结果:
6、总结
(1)当类实现一个接口时,通常使用隐式接口实现,这样可以方便的访问接口方法和类自身具有的方法和属性
(2)当类实现多个接口时,并且接口中包含相同的方法签名,此时使用显式接口实现。即使没有相同的方法签名,仍推荐使用显式接口,因为可以标识出哪个方法属于哪个接口
(3)隐式接口实现,类和接口都可以访问接口中的方法;显式接口实现,只能通过接口访问
(4)隐式接口实现由默认的访问修饰符public,而显式接口实现不能有访问修饰符,但可以看到方法的来源
以上均为个人在学习c#接口时对一些重要知识的总结,希望可以帮到跟我一样的小白。当然,如果本篇文章有什么不足之处,或者造成了一些误导,还希望各位朋友能够指点迷津,谢谢!