C#接口(interface)的用法(非常详细)
C# 程序中,当普通类继承了抽象类后,其实就形成了 IS-A 关系。
例如,我们声明鸟的抽象类 Bird,可以定义飞行抽象方法 Flying(),现在我们创建一个老鹰类 Eagle 继承 Bird 类,然后可以让老鹰类重写飞行方法 Flying(),在这种关系下我们可以说老鹰是一种鸟,所以我们说这是 IS-A 关系。
本节要讲的接口(interface)就比较像是“有同类的行为”。例如,鸟会飞行,飞机也会飞行,然而这是两个完全不同的物种。如果只因为飞行就让鸟类去继承飞机类或是让飞机类去继承鸟类,都是不恰当的,就可以使用接口解决这方面的问题,我们可以设计飞行接口 Fly,然后在这个接口内定义 Flying() 抽象方法,所定义的抽象方法让飞机类和鸟类去实现,这也是接口的基本概念。
程序定义接口时习惯会将第 1 个英文字母用大写,这是为了可以快速区分接口,例如下列接口定义 IAnimal 左边第 1 个英文字母是 I:
【实例 1】最基本的类实现接口的实例。
这个概念也可以应用在接口中,详情可以参考下列实例。
【实例 2】使用 upcasting 概念重新设计实例 1。
使用 upcasting 的限制是只可以调用接口有定义的方法,如果调用类内非接口定义的方法会产生编译错误,可以参考下列实例。
对上例进行扩充,尝试调用非接口方法 Life 有错误产生:
【实例 3】修订实例 1,将 dog 改为是 Dog 类的对象,则程序可以正常运作。
接下来用多个实例讲解接口在不同状况下的应用。
可以用下图来说明上述程序实例:
另外在声明对象时,笔者用了向上转型的方式声明对象,当然读者也可以使用下列方式分别声明 Bird 和 Airplane 类对象:
【实例 5】设计接口 IGrandfather,此接口有 GrandfatherMethod() 方法。然后设计接口 IFather,此接口有 FatherMethod() 方法,同时此接口继承 IGrandfather。然后设计 John 类,此类实现 IFather,同时重写 GrandfatherMethod() 和 FatherMethod() 方法。
【实例 6】设计 IShape 接口,此接口有定义计算面积的方法 Area(),然后设计 Rectangle 类实操 IShape 接口与其方法 Area()。
显式实现的语法如下:
【实例 7】显式实现的实例,读者可以留意第 6 行、第 7 行和第 8 行的调用,如果省略前方的“//”则会有编译错误产生。
【实例 8】ICoord 接口属性 X、Y 和 Distance 的实现,设计一个 Point 类,此 Point 类实现了 X、Y 和 Distance 属性。
多重继承接口的概念,可参考下图,目前一个类可以实现多个接口,一个接口可以继承多个接口。
基本程序设计概念与前文接口的继承与实现概念相同,当一个类继承实现多个接口时,需要实现这些接口的所有抽象方法。当一个接口继承多个接口时,继承此接口的类需要实现此接口及它所有继承接口的抽象方法。
假设 A 类同时继承 B 与 C 接口,整个语法如下:
【实例 9】扩充实例 6,增加 IColor 接口,这个接口定义颜色。
【实例 10】一个接口 IFly 继承了 IBird 和 IAirplane 接口的应用,InfoFly 类将实现 IFly 接口的 PediaFly 抽象方法,以及它所继承的 IAirplane 接口的 AirplaneFly 抽象方法和 IBird 接口的 BirdFly 方法。
虚拟接口方法可以有完整的实体内容,同时此方法不需要在类内实现。使用时需要留意,类并没有继承接口的虚拟接口方法,所以类对象无法调用虚拟接口方法。
【实例 11】虚拟方法的说明,这个程序的重点是类对象无法调用虚拟方法,所以第 9 行若是拿掉“//”会有错误产生。
例如,我们声明鸟的抽象类 Bird,可以定义飞行抽象方法 Flying(),现在我们创建一个老鹰类 Eagle 继承 Bird 类,然后可以让老鹰类重写飞行方法 Flying(),在这种关系下我们可以说老鹰是一种鸟,所以我们说这是 IS-A 关系。
本节要讲的接口(interface)就比较像是“有同类的行为”。例如,鸟会飞行,飞机也会飞行,然而这是两个完全不同的物种。如果只因为飞行就让鸟类去继承飞机类或是让飞机类去继承鸟类,都是不恰当的,就可以使用接口解决这方面的问题,我们可以设计飞行接口 Fly,然后在这个接口内定义 Flying() 抽象方法,所定义的抽象方法让飞机类和鸟类去实现,这也是接口的基本概念。
C#接口的定义
接口外观和类相似但是它不是类,C# 中接口的定义是 interface,特色如下:- 接口可以像类一样拥有方法、属性、索引器和事件,这些方法和属性只是抽象定义,没有主体内容(C#8.0 以后的版本支持可以有主体内容);
- 每个接口的属性和方法都只是定义,一定有类来实现属性或方法,实现也可想成是重写,但是省略 override 关键词;
- 早期 C# 语言接口不可以有字段,C#8.0 以后的版本支持 static 字段;
- 接口成员特色是 public 和 abstract,因为是默认的,所以程序设计时不用再注明是 public 和 abstract。C#8.0 以后的版本有关接口存取修饰词的限制放宽了,也可以有 private、protected、internal、static、sealed、partial 和 virtual,但默认还是 public;
- 每个接口一定有类来实现其方法,实现方式和继承一样使用“:”符号。一个类实现了一个接口,又称一个类继承了一个接口;
- 接口不可以有构造方法;
- 不可以为接口创建对象。
程序定义接口时习惯会将第 1 个英文字母用大写,这是为了可以快速区分接口,例如下列接口定义 IAnimal 左边第 1 个英文字母是 I:
interface IAnimal // 第1个英文字母用大写字母 I { void Action(); // 自动是public和abstract }
【实例 1】最基本的类实现接口的实例。
Dog dog = new Dog(); // 创建 dog 对象 dog.Action(); interface IAnimal // 接口 IAnimal { void Action(); // interface 的 Action 方法 } class Dog : IAnimal // 继承接口 IAnimal { public void Action() // 不含 override { Console.WriteLine("狗:在跑步"); } }执行结果为:
狗:在跑步
C# upcasting实现接口
upcasting 可以翻译为向上转型,基本概念是一个类本质是子类,但是将它当作父类来看待,然后将父类的引用指向子类对象。这个概念也可以应用在接口中,详情可以参考下列实例。
【实例 2】使用 upcasting 概念重新设计实例 1。
IAnimal dog = new Dog(); // Upcasting dog.Action(); interface IAnimal // 接口 IAnimal { void Action(); // interface 的 Action 方法 } class Dog : IAnimal // 继承接口 IAnimal { public void Action() // 不含 override { Console.WriteLine("狗:在跑步"); } }执行结果与实例 1 相同。
使用 upcasting 的限制是只可以调用接口有定义的方法,如果调用类内非接口定义的方法会产生编译错误,可以参考下列实例。
对上例进行扩充,尝试调用非接口方法 Life 有错误产生:
IAnimal dog = new Dog(); // Upcasting dog.Action(); dog.Life(); // error interface IAnimal // 接口 IAnimal { void Action(); // interface 的 Action 方法 } class Dog : IAnimal // 继承接口 IAnimal { public void Action() // 不含 override { Console.WriteLine("狗:在跑步"); } public void Life() // Dog 自有方法 { Console.WriteLine("主人的宠物"); } }执行程序会发现,程序编译错误。
【实例 3】修订实例 1,将 dog 改为是 Dog 类的对象,则程序可以正常运作。
Dog dog = new Dog(); // 创建 Dog类对象 dog dog.Action(); dog.Life(); interface IAnimal // 接口 IAnimal { void Action(); // interface 的 Action 方法 } class Dog : IAnimal // 继承接口 IAnimal { public void Action() // 不含 override { Console.WriteLine("狗:在跑步"); } public void Life() // Dog 自有方法 { Console.WriteLine("主人的宠物"); } }执行结果为:
狗:在跑步
主人的宠物
C#接口的应用场景
每个面向对象的程序语言都有接口功能,这个功能的主要优点如下:- 安全理由,只显示调用方法的名称与参数,隐藏继承类的细节;
- C# 不支持多重继承,一个类只能有一个父类,但是 C# 支持继承多个接口,使用上更具弹性。
接下来用多个实例讲解接口在不同状况下的应用。
1) 两个类实现一个接口
【实例 4】创建一个 Fly 接口,此接口有 Flying() 方法,然后创建 Bird 和 Airplane 类实现 Fly 接口的 Flying() 方法。IFly bird = new Bird(); // upcasting bird.Flying(); IFly airplane = new Airplane(); // upcasting airplane.Flying(); interface IFly { void Flying(); } class Bird : IFly { public void Flying() { Console.WriteLine("Flying:鸟在飞"); } } class Airplane : IFly { public void Flying() { Console.WriteLine("Flying:飞机在飞"); } }执行结果为:
Flying:鸟在飞
Flying:飞机在飞
可以用下图来说明上述程序实例:

另外在声明对象时,笔者用了向上转型的方式声明对象,当然读者也可以使用下列方式分别声明 Bird 和 Airplane 类对象:
Bird f1_y1 = new Bird(); Airplane f1_y2 = new Airplane();
2) 多层次继承与实现
一个接口可以继承另一个接口,使用的方法是“:”,细节可以参考下列实例。【实例 5】设计接口 IGrandfather,此接口有 GrandfatherMethod() 方法。然后设计接口 IFather,此接口有 FatherMethod() 方法,同时此接口继承 IGrandfather。然后设计 John 类,此类实现 IFather,同时重写 GrandfatherMethod() 和 FatherMethod() 方法。
John john = new John(); john.GrandfatherMethod(); john.FatherMethod(); interface IGrandfather // 祖父接口 { void GrandfatherMethod(); } interface IFather : IGrandfather // 父接口继承祖父接口 { void FatherMethod(); } class John : IFather // John类实现父接口 { public void GrandfatherMethod() // 实现祖父方法 { Console.WriteLine("调用 GrandFatherMethod"); } public void FatherMethod() // 实现父方法 { Console.WriteLine("调用 FatherMethod"); } }执行结果为:
调用 GrandFatherMethod 调用 FatherMethod可以用下图来说明上述程序实例:

3) 接口方法内含参数
至今所有实现的接口方法都未含参数,这里主要是创建内含参数的方法,让读者可以实际体验。【实例 6】设计 IShape 接口,此接口有定义计算面积的方法 Area(),然后设计 Rectangle 类实操 IShape 接口与其方法 Area()。
Rectangle rectangle = new Rectangle(); rectangle.Area(5, 10); interface IShape // 定义接口 IShape { void Area(int a, int b); // 定义计算面积 } class Rectangle : IShape // 实现 IShape { public void Area(int a, int b) // 计算面积 { int area = a * b; Console.WriteLine($"矩形面积:{area}"); } }执行结果为:
矩形面积:60
C#接口的显式实现
一个类如果需要设计多个方法实现多个接口方法,使用显式实现可以让整个程序比较容易阅读与了解。显式实现的语法如下:
<InterfaceName>.<MemberName>使用显式实现也是有限制的,非 upcasting 的对象无法调用实现的接口定义的方法。
【实例 7】显式实现的实例,读者可以留意第 6 行、第 7 行和第 8 行的调用,如果省略前方的“//”则会有编译错误产生。
IComputer com = new Software(); // upcasting Software soft = new Software(); // Software类的soft对象 com.Office(); com.Programming("OK"); //soft.Web("NO"); // 编译错误 //soft.Office(); // 编译错误 //soft.WriteFile("NO"); // 编译错误 soft.Web("OK"); interface IComputer { void Office(); void Programming(string text); } class Software : IComputer { void IComputer.Office() // 显式实现 { Console.WriteLine("适用一般职员"); } void IComputer.Programming(string text) // 显式实现 { Console.WriteLine("适用程序设计师"); } public void Web(string text) { Console.WriteLine("适用网页设计"); } }执行结果为:
适用一般职员
适用程序设计师
适用网页设计
C#接口属性的实现
前面重点说明了接口方法的实现,接下来将用实例解说属性的实现。【实例 8】ICoord 接口属性 X、Y 和 Distance 的实现,设计一个 Point 类,此 Point 类实现了 X、Y 和 Distance 属性。
ICoord p = new Point(6, 8); // Upcasting Console.WriteLine($"(x, y)点位置 x = {p.X}, y = {p.Y}"); Console.WriteLine($"与(0, 0)距离 dist = {p.Distance}"); interface ICoord // 接口 { int X { get; set; } // 属性 X int Y { get; set; } // 属性 Y double Distance { get; } // 属性 - 与(0, 0)的距离 } class Point : ICoord // 实现ICoord { public Point(int x, int y) // Constructor { X = x; Y = y; } public int X { get; set; } // 实现属性 X public int Y { get; set; } // 实现属性 Y public double Distance => // 实现属性 Distance Math.Sqrt(X * X + Y * Y); }执行结果为:
(x, y)点位置 x = 6, y = 8
与(0, 0)距离 dist = 10
C#接口的多重继承与实现
所谓的多重继承是指一个类可以继承与实现多个接口,C# 语言的类是不支持多重继承的,不过在接口的实现中可以使用多重继承接口。多重继承接口的概念,可参考下图,目前一个类可以实现多个接口,一个接口可以继承多个接口。

基本程序设计概念与前文接口的继承与实现概念相同,当一个类继承实现多个接口时,需要实现这些接口的所有抽象方法。当一个接口继承多个接口时,继承此接口的类需要实现此接口及它所有继承接口的抽象方法。
假设 A 类同时继承 B 与 C 接口,整个语法如下:
interface IB { void b(); // 抽象方法 b() } interface IC { void c; // 抽象方法 c() } class A implements IB, IC { // 请留意语法 // 实现 b 和 c; }相当于被继承或实操的多个接口之间要有逗号隔开,至于其他规则则不变。
【实例 9】扩充实例 6,增加 IColor 接口,这个接口定义颜色。
Rectangle rectangle = new Rectangle(); rectangle.Area(5, 10); rectangle.Color(); interface IShape // 定义接口 IShape { void Area(int a, int b); // 定义计算面积 } interface IColor // 定义色彩 { void Color(); } class Rectangle : IShape, IColor // 实现 IShape 和 IColor { public void Area(int a, int b) // 计算面积 { int area = a * b; Console.WriteLine($"矩形面积:{area}"); } public void Color() // 定义色彩 { Console.WriteLine("矩形色彩:蓝色"); } }执行结果为:
矩形面积:50
矩形色彩:蓝色

【实例 10】一个接口 IFly 继承了 IBird 和 IAirplane 接口的应用,InfoFly 类将实现 IFly 接口的 PediaFly 抽象方法,以及它所继承的 IAirplane 接口的 AirplaneFly 抽象方法和 IBird 接口的 BirdFly 方法。
InfoFly infofly = new InfoFly(); infofly.BirdFly(); infofly.AirplaneFly(); infofly.PediaFly(); interface IBird // 定义接口 IBird { void BirdFly(); } interface IAirplane // 定义接口 IAirplane { void AirplaneFly(); } interface IFly : IBird, IAirplane // 接口IFly继承IBird和IAirplane { void PediaFly(); } class InfoFly : IFly // 定义类 InfoFly 实作 IFly { public void BirdFly() // 实作 BirdFly { Console.WriteLine("鸟用翅膀飞"); } public void AirplaneFly() // 实作 AirplaneFly { Console.WriteLine("飞机用引擎飞"); } public void PediaFly() // 实作 PediaFly { Console.WriteLine("飞行百科"); } }执行结果为:
鸟用翅膀飞
飞机用引擎飞
飞行百科

C#虚拟接口方法
前面介绍的接口方法均是抽象方法,这些方法需要在继承的类内实现,C# 从 8.0 版本起支持虚拟接口方法,这个方法又称默认方法。虚拟接口方法可以有完整的实体内容,同时此方法不需要在类内实现。使用时需要留意,类并没有继承接口的虚拟接口方法,所以类对象无法调用虚拟接口方法。
【实例 11】虚拟方法的说明,这个程序的重点是类对象无法调用虚拟方法,所以第 9 行若是拿掉“//”会有错误产生。
IComputer com = new Software(); // Upcasting com.Office(); com.Programming("OK"); com.Life(); Software soft = new Software(); // Software类的soft物件 soft.Office(); soft.Programming("OK"); //soft.Life(); // 编译错误 interface IComputer { void Office(); // 定义 Office void Programming(string text); // 定义 Programming void Life() // virtual 方法 { Console.WriteLine("已经是生活必需品"); } } class Software : IComputer { public void Office() // 实作 Office { Console.WriteLine("适用一般职员"); } public void Programming(string text) // 实作 Programming { Console.WriteLine("适用程序设计师"); } }执行结果为:
适用一般职员
适用程序设计师
已经是生活必需品
适用一般职员
适用程序设计师