一、接口定义简介
接口可以简单理解为一种约定,使得实现接口的类或结构在形式上保持一致,使用接口可以使程序更加清晰和条理化。接口是指定一组函数成员而不实现他们的引用类型,所以只能类和结构来实现接口,在继承该接口的类里面要实现接口的所有方法。
在C#语言中,类之间的继承关系仅支持单重继承,而接口是为了实现多重继承关系设计的。(可以理解为接口是C#的灵魂所在),一个类能同时实现多个接口,还能在实现接口的同时再继承其他类,并且接口之间也可以继承。接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同“是什么”部分,派生类定义了语法合同“怎么做”部分。接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。
举个例子:接口可以简单理解为具备某种功能的标准化模块,当某个产品需要有一个自动输出尺寸的功能,我们将输出尺寸定义为接口后,该产品只需要按照接口要求自动调用即可,当我们换了一个新产品,仍然需要输出尺寸的功能,这时候我们只需要再调用对应接口就可以。接口定义了语法合同 “是什么” 部分,派生类定义了语法合同 "怎么做"部分。通俗来讲就是接口定义了一些行为,继承接口的类应该必须拥有这些行为,按照这些行为去做…
1.接口的成员
接口定义了属性、方法和事件,这些都是接口的成员。
interface 接口名称 { 接口成员;//接口成员可以是属性、方法、事件; }
public delegate void TestDelegate(); //定义一个事件 public interface ITestInterface(接口的名字命名一般是大写I开头) { //接口成员 event TestDelegate TestEvent; //事件成员 void FireAway();//方法成员 int Age { get ; set; } //属性成员 }
注意:!!!
接口中定义的成员虽然和类成员类似必须满足以下要求:
(根据命名的规范,一般统一来说接口名称必须从大写的I开始写起)
1.接口中的成员不允许使用 public、private、protected、internal 访问修饰符。所有的接口成员都必须是公共的。
2.但是接口声明可以有任何的访问修饰符,public、protected、internal、private,默认是public。
3.接口中的成员不允许使用 static、virtual、abstract、sealed 修饰符。
4.在接口中不能定义字段。
5.在接口中定义的方法不能包含方法的具体实现。
2.接口调用
接口的调用主要分为两种,显示调用和隐式调用。下面具体介绍调用过程:
1.隐示调用
//接口声明 public interface IEnglish { void Speaker(); } //类继承接口 class people : IEnglish { public void Speaker() //隐式调用必须要有访问修饰符public { Console.WriteLine("我想说中国话"); } } //主函数实现 第一种方式: people pp = new people(); pp.Speaker(); //该种调用不会报错 第二种方式: IEnglish pt = new people(); pt.Speaker();
2.显示调用
public interface IEnglish { void Speaker(); } class people : IEnglish { //不能有public访问修饰符,同时方法的名字必须是接口+方法 如IEnglish.Speaker() void IEnglish.Speaker() { Console.WriteLine("我想说中国话"); } } //主函数实现过程 people pp = new people(); //pp.Speaker(); 该种调用会报错! //正确调用 IEnglish TT = (IEnglish)pp; TT.Speaker();
注意:
1、隐式方式people的成员实现有而且必须有自己的访问修饰符(public),显示实现方式people的成员(Speaker)不能有任何的访问修饰符。
2、显示实现方式people使用接口名称和一个句点命名该类成员(Speaker)来实现的:IEnglish.Speaker();
3.隐示实现对象声明为接口和类都可以访问到其行为,显示实现只有声明为接口可以访问。
4.如果两个接口中有相同的方法名,那么同时实现这两个接口的类,C#提供了显示接口实现技术,就是在方法名前加接口名称,用接口名称来限定成员,用“接口名.方法名()”来区分实现的是哪一个接口。
注意:显示接口实现时,在方法名前不能加任何访问修饰符。这种方式和普通方法不同,普通方法前不加访问修饰符,默认为私有的,而显式接口实现时方法前不加任何修饰符,默认为公有的,如果前面加上修饰符,会出现编译错误。
二、接口属性
1,实现接口的任何类或结构必须实现其所有成员的方法。
2,接口不能直接实例化,但是可以通过指向子类间接实例化。
3,接口可以包含方法和属性的声明,但不能包含字段。
4,接口中所有方法、属性默认为public,不能在后面再添加修饰符。
5,类或结构可以实现多个接口。 类可以继承基类并实现一个或多个
接口。
三、接口特点
1.可以实现多接口继承,也就是一个类可以继承多个接口。
using System; public interface IPerson { string Name { get; set; } void Show(string name); } public interface IStudent { string StudentId { get; set; } void Show(string studentid); } public class Student: IPerson, IStudent //一个类继承了多个接口; { private string _name; public string Name { get { return _name; } set { _name = value; } }
2.当两个接口中有相同命名的方法
using System; namespace ConsoleApp1 { class Program { static void Main(string[] args) { //这三种方法都属于强制转换类型 people pp = new people(); ((IEnglish)pp).Speaker(); ((Ichina)pp).Speaker(); IEnglish pt = new people(); pt.Speaker(); people PM = new people(); IEnglish pl = (IEnglish)PM; pl.Speaker(); Console.ReadKey(); } } public interface IEnglish { void Speaker(); } public interface Ichina { void Speaker(); } class people : IEnglish,Ichina { void IEnglish.Speaker() //隐式调用必须要有访问修饰符public { Console.WriteLine("我会说英语"); } void Ichina.Speaker() //隐式调用必须要有访问修饰符public { Console.WriteLine("我会讲中文"); } } }
具体使用情况如下:
- 参数列表+返回值 都相同:实现类只要实现一次此方法即可
- 参数列表相同+返回值不同:实现类无法直接实现两个方法(IDE报错),因为不满足方法重载原则
- 参数列表不相同:实现类可以分别实现两个方法,可以方法重载
3.接口和父类中名字相同时
1.若子类没有重写该方法,则默认会优先调用父类中的方法,不会报错。
interface A{ public void run(); } class B:A { public void run(){ Console.WriteLine("B中的run()方法"); } } public class Test01 { public static void main(String[] args) { Test t = new Test(); t.run(); } } /* 输出: B中的run()方法 */
2.若子类重写了该方法,通过显示实现可以调用接口中的方法,否则调用的仍然是该类中固有的方法。
using System; namespace ConsoleApp1 { class Program { static void Main(string[] args) { people pp = new people(); pp.Speaker(); IEnglish pt = new people(); pt.Speaker(); Console.ReadKey(); } } public interface IEnglish { void Speaker(); } class people : IEnglish { public void Speaker() //隐式调用必须要有访问修饰符public { Console.WriteLine("我会说英语"); } void IEnglish.Speaker() { Console.WriteLine("我会说中文"); } } }
四、接口和抽象类的区别
接口与抽象类非常相似,它定义了一些未实现的属性和方法。所有继承它的类都继承这些成员,在这个角度上,可以把接口理解为一个类的模板。接口最终的目的是起到统一的作用。
1,两者都包含可以由子类继承的抽象成员;
2,两者都不直接实例化。
3,抽象类除拥有抽象成员之外,还可以拥有非抽象成员;而接口所有的成员都是抽象的。
4,抽象成员可以是私有的,而接口的成员默认是公有的。
5,接口中不能含有构造函数、析构函数、静态成员和常量。
6、接口支持多继承,抽象类不能实现多继承。
7、接口只能定义抽象规则,抽象类既可以定义规则,还可能提供已实现的成员。
8、接口是一组行为规范,抽象类是一个不完全的类。
9、接口可以用于支持回调,抽象类不能实现回调,因为继承不支持。
10、接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法,抽象类可以定义字段、属性、包含有实现的方法。
11、接口可以作用于值类型和引用类型,抽象类只能作用于引用类型。例如,Struct就可以继承接口,而不能继承类。
总结起来说,使用C#接口应注意几个问题:
1、C#中的接口是独立于类来定义的。这与 C++模型是对立的,在 C++中接口实际上就是抽象基类。
2、接口和类都可以继承多个接口。
3、类可以继承一个基类,接口根本不能继承类。这种模型避免了 C++的多继承问题,C++中不同基类中的实现可能出现冲突。因此也不再需要诸如虚拟继承和显式作用域这类复杂机制。C#的简化接口模型有助于加快应用程序的开发。
4、一个接口定义一个只有抽象成员的引用类型。C#中一个接口实际所做的,仅仅只存在着方法标志,但根本就没有执行代码。这就暗示了不能实例化一个接口,只能实例化一个派生自该接口的对象。
5、接口可以定义方法、属性和索引。所以,对比一个类,接口的特殊性是:当定义一个类时,可以派生自多重接口,而你只能可以从仅有的一个类派生。