🐸 青蛙编程网
首页 / C# / 面向对象 / 4. 接口

接口

这是什么:接口(interface)——一份"能力合同",只规定要会做什么,不规定怎么做。

为什么用:继承讲的是"我是你儿子,继承你的家产",可八竿子打不着的真狗和机器狗也都得会叫。接口让它们签同一份《会叫合同》,谁也不用当谁的儿子。

如何用:本章先写一份 IAnimal 接口,只写方法不写方法体,再让 Dog 签下它。

一纸合同:interface

接口只写"要会什么",不写"怎么做"——方法连方法体都没有:

interface IAnimal        // 惯例:接口名用 I 开头
{
    string Name { get; }    // 必须有一个只读的 Name
    void Speak();           // 必须会叫——至于怎么叫,合同不管
}

合同里只有成员的"形状",没有任何实现代码。

谁签约,谁实现

类用冒号 : 签约(和继承一个写法),然后必须把合同里的每一条都实现出来:

// 用接口类型装对象:只要是签约方,都能往里装
List<IAnimal> pets = new List<IAnimal>
{
    new Dog("旺财"),
    new RobotDog()
};

foreach (IAnimal pet in pets)
{
    Console.WriteLine($"{pet.Name} 报到!");
    pet.Speak();
}

class Dog : IAnimal                 // 签约:Dog 承诺实现 IAnimal 的一切
{
    public string Name { get; }
    public Dog(string name) => Name = name;
    public void Speak() => Console.WriteLine($"{Name}:汪汪!");
}

class RobotDog : IAnimal            // 和 Dog 毫无血缘,照样签约
{
    public string Name { get; } = "机器狗";
    public void Speak() => Console.WriteLine($"{Name}:汪!汪!(电子音)");
}

interface IAnimal
{
    string Name { get; }
    void Speak();
}

运行输出:

旺财 报到!
旺财:汪汪!
机器狗 报到!
机器狗:汪!汪!(电子音)

为什么要面向接口编程

对比 继承 class 接口 interface
关系 是一种(is-a),有血缘 会一套(can-do),签合同
数量 只能继承一个父类 可以同时实现多个接口
内容 字段 + 属性 + 方法(带实现) 只有成员声明(合同条款)

注意 List<IAnimal>:这个列表根本不关心里面装的是啥,只要求签约。明天店里来一只会叫的电子鸭子,写个新类签约,循环代码一个字不改就能用。代码依赖"合同"而不是"具体某一家",换实现、加功能都不伤筋动骨——这就是面向接口编程。

再签一份合同:IPettable

一个类可以同时实现多个接口,用逗号隔开:

interface IPettable
{
    void Pet();  // 抚摸
}

class Dog : IAnimal, IPettable   // Dog 同时签两份合同
{
    public string Name { get; }
    public Dog(string name) => Name = name;
    public void Speak() => Console.WriteLine($"{Name}:汪汪!");
    public void Pet() => Console.WriteLine($"{Name} 舒服地摇尾巴");
}

// 用法
IPettable p = new Dog("旺财");
p.Pet();  // 旺财 舒服地摇尾巴

记忆口诀

接口合同不带肉,签约类来补实现;List 只认 I 开头,谁来都往里装。

❓ 常见问题

Q1:接口里能写方法实现吗?

C# 8.0 开始支持接口里的默认方法实现(default),但入门阶段把接口理解成"纯合同"即可:只声明,不实现。具体实现由签约类负责。

Q2:类能同时继承一个父类和实现多个接口吗?

可以。写法:class Dog : Animal, IAnimal, IPettable——冒号后第一个是父类,后面是接口,用逗号隔开。

Q3:接口名为什么要加 I 前缀?

这是 C# 的惯例(convention),不是强制规范。IAnimal 一眼就能看出是接口,不是普通类。团队里统一遵循即可。

⚠️ 新手翻车现场

翻车一:签约类没实现合同里的所有成员

class Dog : IAnimal
{
    public string Name { get; }
    public Dog(string name) => Name = name;
    // 漏掉了 Speak()!❌
}

问题:实现接口时,合同里的每一条都必须实现,漏一条编译就报错。

修正:补上 public void Speak() => ...。

翻车二:接口变量调用接口里没有的成员

IAnimal pet = new Dog("旺财");
pet.Bark();  // ❌ IAnimal 合同里没有 Bark,编译报错

问题:接口类型的变量只能调用接口里定义的成员。要调 Bark,得把变量类型改成 Dog,或者强制转换。

修正:Dog d = new Dog("旺财"); d.Bark();

翻车三:把接口当类 new

IAnimal a = new IAnimal();  // ❌ 接口不能直接实例化

问题:接口是合同,不是类,不能 new。必须 new 一个签约类(如 new Dog())。

修正:IAnimal a = new Dog("旺财");

🎯 什么时候用接口

场景 推荐 原因
不相关的类需要同一能力 接口 狗和机器狗没血缘,都能签 IAnimal
一个类要有多种能力 接口 可同时实现多个接口
代码只关心"能做什么",不关心"是谁" 接口 List<IAnimal> 装任何签约方

📝 本章小结

  • 接口是合同,只声明成员,不写实现
  • 类用 : 签约,必须实现合同里所有成员
  • 一个类可实现多个接口,用逗号隔开
  • 接口变量只能调接口里定义的成员
  • 接口不能直接 new,得 new 签约类
  • 记忆口诀:接口合同不带肉,签约类来补实现;List 只认 I 开头,谁来都往里装。

动手改

  1. 写一个 Cat : IAnimal,把它也装进 pets,验证循环不用改
  2. 再定义一个 IPettable 接口(约定 void Pet()),让 Dog 同时签两份合同
  3. 删掉 Dog.Speak 的实现再编译,看看编译器开出什么罚单

下一章:综合练习,开一家宠物店把五招全用上 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。