Keep on going never give up.

Let's Go

C# 学习笔记(42)接口

C#Lonely2019-05-18 00:15:1634次0条

什么是接口?

接口可以理解为对一组方法进行声明进行的统一命名,但这些方法没有提供任何的实现。也就是说,把一组方法声明在一个接口中,然后继承于该接口的类都需要实现这些方法。

例如,很多类型(比如int类型、string类型和字符类型等)都需要比较大小,此时可以定义一个比较接口,在该接口的中定义比较方法,然后让这些类型去继承接口,并实现自己的比较方法。

通过接口,你可以对方法进行统一管理,避免了在每种类型中重复定义这些方法。

接口的定义

接口的定义与类的定义非常相似,只是使用的关键字不一样罢了,类的定义使用class关键字,而接口使用interface关键字进行定义。在Visual Studio中添加接口的步骤如下:右击项目,在弹出的菜单中选择“添加”--> “新建项”菜单项,在弹出的窗口中选择“接口”并命名接口名称,最后点击添加按钮,这样就完成了接口的添加。

image.png


接口定义的语法:

[访问修饰符] interface 接口名

{

//接口成员定义

}

下面这段代码就是一个简单接口的定义,它并没有定义任何方法。

interface IFlyable
{

}

下面向该接口添加一个方法,以让所有继承该接口的类都可以实现这个方法,添加方法之后的代码如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp
{

    /// <summary>
    /// 飞行接口
    /// </summary>
    interface IFlyable
    {
        //接口中的成员不能加访问修饰符,默认为public,不能修改。
        void Fly(); //定义飞行方法,继承该接口的类都要实现该方法       
    }
}

定义完接口之后,添加Bird、Aircraft、Meteor三个类来继承接口,如果有类想要继承该接口,则它必须实现接口中定义的所有方法。类继承接口与继承类是一样的,只需要使用英文的冒号“:”,后接接口名称就可以了。代码如下所示:

/// <summary>
/// 鸟
/// </summary>
public class Bird : IFlyable //继承接口
{
    public void Fly() //实现接口方法
    {
        Console.WriteLine("鸟能够在天上飞");
    }
}

/// <summary>
/// 飞机
/// </summary>
public class Aircraft : IFlyable //继承接口
{
    public void Fly() //实现接口方法
    {
        Console.WriteLine("飞机能够在天上飞");
    }
}

/// <summary>
/// 风筝
/// </summary>
public class Kite: IFlyable //继承接口
{
    public void Fly() //实现接口方法
    {
        Console.WriteLine("风筝能够在天上飞");
    }
}

调用接口中的方法代码如下所示:

IFlyable[] fly = { new Bird(), new Aircraft(), new Kite() };
for (int i = 0; i < fly.Length; i++)
{
    fly[i].Fly(); //调用接口中的方法
}
Console.ReadKey();

运行结果:

image.png

显式接口实现方式

显式接口实现指的是使用接口名作为方法名的前缀,这称为“显式接口实现”;传统的实现方式,称为“隐式接口实现”。

当多个接口中包含相同方法名称、相同返回类型和相同参数时,如果一个类同时实现了这些接口,隐式接口实现就会出现命名冲突的问题,下面的代码对比进行了演示。

/// <summary>
/// 中文接口
/// </summary>
interface IChinese
{
    void SayHello(); //打招呼
}

/// <summary>
/// 英文接口
/// </summary>
interface IEnglish
{
    void SayHello(); //打招呼
}

// 我会用中文打招呼也会用英文打招呼,实现了两个接口
public class Me : IChinese, IEnglish
{
    //隐式接口实现
    public void SayHello()
    {
        Console.WriteLine("你好");
    }
}

以上代码中定义了一个Me类,它实现了两个接口,并且这两个接口中声明的方法具有相同的返回类型、相同的方法名称和相同的参数(这里都没有参数)。若采用隐式的接口实现方式,下面的代码将调用相同的SayHello方法,而不管具体是获取了哪个接口。

代码示例:

//调用接口中的方法
//使用隐式接口实现的方法有两种调用方法,即直接用类和接口都可以调用SayHello方法。

//方法一(使用类调用):
Me me = new Me();
me.SayHello();

//方法二(使用接口调用):
IChinese chinese = new Me();
chinese.SayHello();

IEnglish english = new Me();
english.SayHello();

Console.ReadKey();

运行结果:

image.png

从运行结果可以发现,不管获得了哪个接口,程序都是调用的同一个方法实现,即都是输出“你好”。但我们期望的结果并不是这样,我们希望调用IChinese接口的SayHello方法可以输出“你好”,而调用IEnglish接口的SayHello方法可以输出“Hello”。

显式的接口实现方法可以解决这样的命名冲突问题,下面代码演示显式接口实现的方法。

代码示例:

/// <summary>
/// 中文接口
/// </summary>
interface IChinese
{
    void SayHello(); //打招呼
}

/// <summary>
/// 英文接口
/// </summary>
interface IEnglish
{
    void SayHello(); //打招呼
}

// 我会用中文打招呼也会用英文打招呼,实现了两个接口
public class Me : IChinese, IEnglish
{
    //显式的接口实现
    void IChinese.SayHello() //不允许使用任何访问修饰符,显式实现的成员都默认为私有
    {
        Console.WriteLine("你好");
    }
    
    //显式的接口实现
    void IEnglish.SayHello() //不允许使用任何访问修饰符,显式实现的成员都默认为私有
    {
        Console.WriteLine("Hello");
    }        
}

调用代码:

//调用接口中的方法
//使用显式接口实现的方法只能通过接口来调用SayHello方法,不能通过类来调用。
IChinese chinese = new Me();
chinese.SayHello();

IEnglish english = new Me();
english.SayHello();

Console.ReadKey();

运行结果:

image.png

由此可见,显式的接口实现解决了命名冲突问题,程序根据不同接口输出了不同的打招呼内容。

在使用显式的接口实现方式时,需要注意以下几个问题:

1、若显式实现接口,方法中不能使用任何访问修饰符,显式实现的成员都默认为私有

2、显式实现的成员默认是私有的,所以这些成员都不能通过类的对象进行访问。


总结:

1、接口是指定一组函数成员而不实现它们的引用类型,这是是一种规范。

2、接口的定义使用interface关键字,接口的名称一般以I开头able结尾,表示“我能做”的意思,例如微软类库中的IComparable接口、ICloneable接口等。

3、只能使用类和结构来实现接口,实现接口的子类必须实现该接口的全部成员。

4、为了多态,接口不能被实例化。也就是说,不能new接口(不能创建对象)。

5、接口中所有的成员都默认是公共的(即默认为public),因此不能接口中的成员不能加访问修饰符如:public、private和protected等,也不能使用static修饰符。如果显式地指定了访问修饰符,则会出现编译时错误。

6、接口中只能有方法、属性、事件、索引器,接口中不能包含字段、运算符重载、实力构造函数、析构函数。如果在接口中定义了不该包含的成员,则会导致编译错误。

7、接口中的成员不能有任何实现(即“光说不做”,只是定义了一组未实现的成员)。

8、接口与接口之间可以继承,并且可以多继承。接口并不能去继承一个类,而类可以继承接口(即接口只能继承接口,而类既可以继承接口,也可以继承类)。

9、类是单继承的,如果一个类即继承了类又实现了接口,那么在语法上类必须写在接口前面。例如:public class 子类:父类,接口,接口{ }

10、当一个抽象类继承接口的时候,可以不实现接口中的方法,但是必须将其定义为公共的抽象方法,继承该抽象类的子类必须实现抽象父类中的所有抽象方法。


显式实现接口的目的:解决方法重名的问题。若显式实现接口,方法中不能使用任何访问修饰符,显式实现的成员都默认为私有,所以这些成员都不能通过类的对象进行访问。

隐式接口实现与显式接口实现的区别:使用隐式接口实现时,类和接口都可以访问接口中的方法。使用显式接口实现时,只能通过接口来访问接口中的方法,因为此时接口方法默认为私有。

什么时候使用隐式接口实现?什么时候使用显式接口实现?

当类实现单个接口时,通常使用隐式接口实现方法,这样类的对象可以直接去访问接口方法。

当类实现了多个接口,并且接口中包含相同的方法名称、参数和返回类型时,则应该使用显式接口实现方法。即使没有相同的方法签名,在实现多个接口时,仍推荐使用显式的方式,因为这样可以标识出哪个方法属于哪个接口。显式接口实现语法:返回类型 接口名.方法名()


接口与抽象类的区别?

1、抽象类使用abstract关键字进行定义,而接口使用interface关键字进行定义,两者都不能进行实例化。

2、抽象类中可以包含虚方法、非抽象成员和静态成员,但接口中不能包含虚方法和任何静态成员,并且接口中只能定义方法,不能有具体实现,方法的具体实现由实现类完成。

3、抽象类不能实现多继承,接口则可以多继承。注意,从严格意义上说,类继承接口应该成为类实现接口。

4、抽象类是一类对象的抽象,继承于抽象类的类与抽象类为属于关系;而类实现接口只是代表实现类具有接口声明的方法,是一种can-do关系。所以一般接口后都带有able字段,表示“我能做”的意思。


暗锚,解决锚点偏移

文章评论

    嘿,来试试登录吧!