首页 > 编程笔记 > C#笔记 阅读:38

C#接口(interface)的用法(非常详细)

C# 程序中,当普通类继承了抽象类后,其实就形成了 IS-A 关系。

例如,我们声明鸟的抽象类 Bird,可以定义飞行抽象方法 Flying(),现在我们创建一个老鹰类 Eagle 继承 Bird 类,然后可以让老鹰类重写飞行方法 Flying(),在这种关系下我们可以说老鹰是一种鸟,所以我们说这是 IS-A 关系。

本节要讲的接口(interface)就比较像是“有同类的行为”。例如,鸟会飞行,飞机也会飞行,然而这是两个完全不同的物种。如果只因为飞行就让鸟类去继承飞机类或是让飞机类去继承鸟类,都是不恰当的,就可以使用接口解决这方面的问题,我们可以设计飞行接口 Fly,然后在这个接口内定义 Flying() 抽象方法,所定义的抽象方法让飞机类和鸟类去实现,这也是接口的基本概念。

C#接口的定义

接口外观和类相似但是它不是类,C# 中接口的定义是 interface,特色如下:
程序定义接口时习惯会将第 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#接口的应用场景

每个面向对象的程序语言都有接口功能,这个功能的主要优点如下:
接下来用多个实例讲解接口在不同状况下的应用。

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

上述程序第 1 行使用 Upcasting 创建对象 p 后,会对 (6, 8) 使用 Point 构造方法创建 X、Y 和 Distance 属性,程序第 3 行和第 4 行则分别输出这些属性。

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("适用程序设计师");
    }
}
执行结果为:

适用一般职员
适用程序设计师
已经是生活必需品
适用一般职员
适用程序设计师

相关文章