目录

C知识学习-011interface

C#知识学习-011(interface)

1.本质

接口 (interface) 是一个纯粹的规范。​​ 它只定义 ​​“必须有什么”​​,不定义 ​​“具体怎么做”

想象一下你要买一个“能播放音乐的设备”。你不在乎它是手机、MP3 播放器还是电脑,你只在乎它​​必须能做​​一件事:​​播放音乐​​。

  • 接口 (interface)​​ 就像这份“设备功能说明书”。它只​​规定​​这个设备​​必须有哪些功能(方法)​​,比如 PlayMusic()
  • ​类 (class)、记录 (record)、结构 (struct)​​ 就像是具体的设备制造商(比如苹果、索尼)。它们​​签了这份合同​​(实现了接口),就必须​​按照说明书的要求​​,在自己的产品(类)里​​具体实现​​ PlayMusic()这个方法。苹果手机怎么播放音乐(用扬声器?用蓝牙?)和索尼播放器怎么播放(插耳机?)是它们各自内部的事情,但说明书只要求“能播放”这个结果。

2.目的

2.1 多态性

你可以使用接口类型的变量来引用任何实现了该接口的具体类型的对象。通过这个接口变量,你只能调用接口中定义的那些成员同一个接口调用,在不同对象上会产生不同的行为(因为具体实现不同)。

解释:这个概念比较抽象,我会举一个具体的例子帮你理解它

假如你有一个​​万能遥控器​​。这个遥控器上只有几个基本按钮:​​开机​​、​​关机​​。

接口就是遥控器的按钮说明书:​


public interface IRemoteControl
{
    void TurnOn();  // 开机按钮的规格
    void TurnOff(); // 关机按钮的规格
}

具体类型的对象就是不同的电器:​


public class TV : IRemoteControl // 电视机承诺能用遥控器控制
{
    public void TurnOn()
    {
        Console.WriteLine("电视机:屏幕亮起...");
    }
    public void TurnOff()
    {
        Console.WriteLine("电视机:屏幕变黑,进入待机状态...");
    }
}

public class AirConditioner : IRemoteControl // 空调也承诺能用遥控器控制
{
    public void TurnOn()
    {
        Console.WriteLine("空调:滴一声,开始吹风...");
    }
    public void TurnOff()
    {
        Console.WriteLine("空调:停止送风,进入休眠...");
    }
}

现在开始分解刚才的概念:

​“你可以使用接口类型的变量(就像你手里的​​万能遥控器)来引用任何实现了该接口的具体类型的对象(就像​​电视机或 空调 )。”​

用接口变量引用具体对象:​​ 你可以用同一个遥控器,先让它​​指向​​电视机,再让它​​指向​​空调。


// 声明一个“遥控器类型”的变量 remote,IRemoteControl(接口类型)
IRemoteControl remote; 

remote = new TV();      // remote “指向” 电视机对象 (万能遥控器对准电视)
// ... 稍后 ...
remote = new AirConditioner(); // remote “指向” 空调对象 (万能遥控器对准空调)

​“通过这个接口变量,你只能调用接口中定义的那些成员。”​

当你拿着万能遥控器时,你​​只能按说明书 (IRemoteControl) 上定义的按钮​​:TurnOn()和 TurnOff()。你​​不能​​通过 remote去:

  • 调电视机的频道,因为遥控器说明书上没这个按钮。
  • 调空调的温度 ,因为遥控器说明书上也没这个按钮。

​“同一个接口调用(你按下了遥控器上的同一个 TurnOn()按钮),在不同对象上会产生不同的行为

  • 当 remote​指向电视机时,按下 TurnOn():电视机:屏幕亮起
  • 当 remote​指向空调​​时,按下​​同一个​​ TurnOn()按钮:// 输出:空调:滴一声,开始吹风

虽然你按的是遥控器上​​同一个按钮 (TurnOn())​​,但这个按钮信号发送到了​​不同的电器对象​​。电视机对象内部的 TurnOn()方法代码是让电视开机,空调对象内部的 TurnOn()方法代码是让空调开机。​​具体怎么“开机”,是由那个被指向的对象自己决定的!​

2.2 解耦

**代码可以依赖于接口(规范),而不是依赖于具体的实现类。**这使得代码更灵活,更容易修改和扩展。你可以更换实现类而不影响依赖于接口的代码。

  • 解释:写代码的人(比如写打开程序的人)只需要知道“这东西能打开”(它实现了 TurnOn接口),不需要知道空调或者电视怎么打开的具体细节。这样代码更灵活,更易更换设备类型。

3.关键特点​

3.1 只声明,不实现​

  • 接口主要用来​​定义方法名、参数和返回值类型​​,但不写方法里面具体做什么。

  • 接口声明了一组成员(方法、属性、事件、索引器)的签名。​

    • ​签名:​​ 成员的名字、参数类型(如果有)、返回值类型(如果有)。
    • 没有实现:​​ 接口本身不包含这些成员的具体代码逻辑。
  • 例子:​

    
    interface IPlayMusic
    {
        void Play(); // 声明一个Play 的方法,没有参数,不返回值(void)
        void Stop(); // 声明一个Stop 的方法
        string CurrentSong { get; } // 声明一个只读属性,获取当前歌曲名
    }

    这个 IPlayMusic接口规定:任何签了这份合同的设备(类),都必须有 Play()Stop()方法和一个 CurrentSong属性。

3.2 实现接口的类必须提供具体实现

  • ​如果一个类型声明实现了某个接口,它就必须为该接口中声明的每一个成员(没有默认实现的)提供具体的实现代码。​​ 这是编译器强制要求的。​

  • 类、结构、记录可以通过 :语法声明它们实现了某个或多个接口。​

  • 例子:

    
    class SmartSpeaker : IPlayMusic // SmartSpeaker 类实现了 IPlayMusic 接口
    {
        private string _currentSong; // 内部字段,存储当前歌曲
    
        // 实现接口要求的 Play 方法
        public void Play()
        {
            Console.WriteLine("智能音箱开始播放音乐...");
            // 音箱播放音乐的具体代码
        }
    
        // 实现接口要求的 Stop 方法
        public void Stop()
        {
            Console.WriteLine("智能音箱停止播放音乐。");
            // 音箱停止播放的具体代码
        }
    
        // 实现接口要求的 CurrentSong 只读属性
        public string CurrentSong
        {
            get { return _currentSong; }
        }
    }

4.使用接口

你可以创建一个接口类型的变量,然后用实现了该接口的类的实例来赋值。通过这个接口变量,你只能调用接口中定义的那些方法/属性。

例子:​


// 创建一个 IPlayMusic 类型的变量,指向一个 SmartSpeaker 对象
IPlayMusic myPlayer = new SmartSpeaker();

myPlayer.Play(); // 调用 Play 方法 -> 输出 "智能音箱开始播放音乐..."
Console.WriteLine("播放: " + myPlayer.CurrentSong); // 访问CurrentSong属性
myPlayer.Stop(); // 调用 Stop 方法 -> 输出 "智能音箱停止播放音乐。"

// 注意:如果 SmartSpeaker 自己有个 VolumeUp() 方法
//你不能直接用 myPlayer.VolumeUp() 调用

关键点:​​ myPlayer变量的类型是 IPlayMusic。这意味着无论它后面实际指向的是 SmartSpeakerPhone还是 MP3Player对象,**只要这些对象实现了 IPlayMusic,我就可以安全地调用 Play()Stop()CurrentSong。**我不需要关心具体是哪种设备在播放。

5.显式接口实现​

有时候,一个类实现了多个接口,而这些接口可能有​​同名同参数​​的方法。或者,你想​​隐藏​​接口方法的实现,让它只能通过接口访问,不能通过类实例直接访问。

语法:在方法名前加上接口名和一个点 .

显式实现解决了名字冲突的问题,并且把接口方法的访问限制在了通过接口引用的方式。

例子:​


interface IDrawOnScreen
{
    void Draw();
}

interface IDrawOnPaper
{
    void Draw();
}

class MultiPurposePrinter : IDrawOnScreen, IDrawOnPaper
{
    // 显式实现 IDrawOnScreen.Draw
    void IDrawOnScreen.Draw()
    {
        Console.WriteLine("Drawing on the screen...");
    }

    // 显式实现 IDrawOnPaper.Draw
    void IDrawOnPaper.Draw()
    {
        Console.WriteLine("Printing on paper...");
    }
}

只能通过接口访问:


MultiPurposePrinter printer = new MultiPurposePrinter();

// 通过接口调用显式实现的方法
IDrawOnScreen screenPrinter = printer;
screenPrinter.Draw(); // 输出 "Drawing on the screen..."

IDrawOnPaper paperPrinter = printer;
paperPrinter.Draw(); // 输出 "Printing on paper..."

// 不能直接用 printer 调用 IDrawOnScreen.Draw 或 IDrawOnPaper.Draw
// printer.IDrawOnScreen.Draw(); // 错误!

6.接口成员​

6.1 默认接口成员​

  • 以前接口成员绝对不能有实现。现在允许接口为方法或属性提供​​默认实现​​。

  • 实现这个接口的类​​可以选择​​使用这个默认实现,也可以自己​​重写​​它。

  • 例子:​

    
    // 开关接口(带默认实现)
    interface ISwitchable
    {
        // 默认实现:通用的打开方式
        void TurnOn() 
        {
            Console.WriteLine("设备已启动");
        }
    }
    
    // 灯泡选择使用默认实现(不用自己写TurnOn)
    class LightBulb : ISwitchable
    {
        // 这里什么都不用写!自动继承默认实现
    }
    
    // 风扇选择重写默认实现(提供自己的特殊实现)
    class Fan : ISwitchable
    {
        // 显式重写默认实现
        public void TurnOn() 
        {
            Console.WriteLine("风扇转动");
        }
    }

    使用时:

    
    ISwitchable bulb = new LightBulb();
    bulb.TurnOn(); // 输出:设备已启动 (使用默认实现)
    
    ISwitchable fan = new Fan();
    fan.TurnOn(); // 输出:风扇转动 (使用自定义实现)

​6.2 静态成员

静态成员**(static)不属于任何实例​**​,通过接口名直接访问,举例:


public interface IShape
{
    static readonly double PI = 3.14159;// 静态成员:一个常量 (PI)
}

使用:


double piValue = IShape.PI; // 直接通过接口名 IShape 访问

学到了这里,咱俩真棒,记得按时吃饭(希望你今天开心~,明天也是)

【本篇结束,新的知识会不定时补充】

感谢你的阅读!如果内容有帮助,欢迎 ​​点赞❤️ + 收藏⭐ + 关注​​ 支持! 😊