19-接口(Interface)
接口定义了一组方法签名,但不提供实现。实现接口的类必须实现接口中定义的所有成员。接口描述了"能做什么"(能力),而不是"是什么"(类型)。
一、接口 vs 抽象类 vs 类
| 对比项 | 类(Class) | 抽象类(Abstract Class) | 接口(Interface) |
|---|---|---|---|
| 实例化 | 能 | 不能 | 不能 |
| 方法实现 | 全部有实现 | 部分有实现 | 全部无实现(C# 8.0+ 可有默认实现) |
| 字段 | 可以有 | 可以有 | 不能有 |
| 构造函数 | 可以有 | 可以有 | 不能有 |
| 多继承 | 不支持 | 不支持 | 支持(一个类可实现多个接口) |
| 使用场景 | "是什么" | "是什么" + "部分默认行为" | "能做什么"(能力合约) |
二、接口的定义和实现
定义接口
csharp
// 命名规范:以大写 I 开头
public interface IFlyable
{
void Fly(); // 方法签名,没有方法体
void Land();
}
public interface ISwimmable
{
void Swim();
}实现接口
实现接口的类必须实现所有接口成员。
csharp
// 实现单个接口
public class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("鸟儿在飞翔");
}
public void Land()
{
Console.WriteLine("鸟儿着陆");
}
}
// 实现多个接口(使用逗号分隔)
public class Duck : IFlyable, ISwimmable
{
public void Fly()
{
Console.WriteLine("鸭子飞起来了");
}
public void Land()
{
Console.WriteLine("鸭子着陆");
}
public void Swim()
{
Console.WriteLine("鸭子在游泳");
}
}
// 使用
IFlyable bird = new Bird();
bird.Fly();
bird.Land();
Duck duck = new Duck();
duck.Fly(); // 类自身方法
duck.Swim(); // 类自身方法
// 通过接口引用调用
IFlyable flyable = duck;
flyable.Fly(); // 只能调用 IFlyable 的方法
ISwimmable swimmable = duck;
swimmable.Swim(); // 只能调用 ISwimmable 的方法三、接口作为参数和返回值
接口最大的威力在于可以编写面向接口的代码,与具体实现解耦。
csharp
// 定义日志接口
public interface ILogger
{
void Log(string message);
}
// 实现1:控制台日志
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine($"[Console] {DateTime.Now}: {message}");
}
}
// 实现2:文件日志
public class FileLogger : ILogger
{
private string path;
public FileLogger(string path)
{
this.path = path;
}
public void Log(string message)
{
string line = $"[File] {DateTime.Now}: {message}";
File.AppendAllText(path, line + Environment.NewLine);
}
}
// 实现3:空日志(不执行任何操作)
public class NullLogger : ILogger
{
public void Log(string message) { }
}
// 函数接受接口参数——与具体实现解耦
public class OrderService
{
private ILogger logger;
// 依赖注入:通过构造函数传入具体实现
public OrderService(ILogger logger)
{
this.logger = logger;
}
public void CreateOrder(string orderId)
{
// 业务逻辑...
logger.Log($"订单 {orderId} 已创建");
}
}
// 使用
var service1 = new OrderService(new ConsoleLogger());
service1.CreateOrder("ORD-001"); // 控制台输出
var service2 = new OrderService(new FileLogger(@"C:\log.txt"));
service2.CreateOrder("ORD-002"); // 输出到文件
var service3 = new OrderService(new NullLogger());
service3.CreateOrder("ORD-003"); // 无输出四、显式接口实现
当类实现多个接口且方法签名冲突时,使用显式接口实现来区分。
csharp
public interface IWriter
{
void Write();
}
public interface IReader
{
void Write(); // 同名方法
}
public class Device : IWriter, IReader
{
// 显式接口实现:方法名前加接口名
void IWriter.Write()
{
Console.WriteLine("IWriter:写入数据");
}
void IReader.Write()
{
Console.WriteLine("IReader:读取数据");
}
// 公有方法
public void Write()
{
Console.WriteLine("Device:默认操作");
}
}
// 使用
Device device = new Device();
device.Write(); // 调用公有方法:Device:默认操作
IWriter writer = device;
writer.Write(); // 调用显式实现:IWriter:写入数据
IReader reader = device;
reader.Write(); // 调用显式实现:IReader:读取数据显式接口实现的特点
- 只能通过接口引用调用,不能通过类实例调用
- 方法默认是
private的(不需要也不能写访问修饰符) - 常用于解决多接口方法冲突
- 也用于隐藏"不常用"的接口成员
五、接口继承
接口之间可以相互继承。
csharp
public interface IWorkable
{
void Work();
}
public interface IManageable : IWorkable // 接口继承
{
void Manage();
}
// 实现类必须实现所有接口的成员
public class Manager : IManageable
{
public void Work() // 来自 IWorkable
{
Console.WriteLine("经理在工作");
}
public void Manage() // 来自 IManageable
{
Console.WriteLine("经理在管理");
}
}
// 或者同时实现多个接口(效果类似)
public class Worker : IWorkable, IDisposable
{
public void Work() { }
public void Dispose() { }
}六、接口中的属性
csharp
public interface IAnimal
{
string Name { get; set; } // 可读写属性
int Age { get; } // 只读属性
string Species { get; } // 只读属性
void Speak();
}
public class Dog : IAnimal
{
public string Name { get; set; }
public int Age { get; }
public string Species => "犬科";
public Dog(int age)
{
Age = age;
}
public void Speak()
{
Console.WriteLine($"{Name} 在汪汪叫");
}
}七、内置接口(常用)
| 接口 | 作用 | 典型使用 |
|---|---|---|
IComparable<T> | 定义比较方法用于排序 | list.Sort() 要求元素实现 |
IEquatable<T> | 定义相等比较 | Dictionary 键比较 |
IDisposable | 释放非托管资源 | using 语句 |
IEnumerable<T> | 支持 foreach 遍历 | 所有集合类 |
ICloneable | 支持克隆 | obj.Clone() |
IFormattable | 支持格式化字符串 | ToString("N2") |
IDisposable 示例
csharp
public class FileManager : IDisposable
{
private FileStream? stream;
public void Open(string path)
{
stream = File.OpenRead(path);
}
public void Dispose()
{
stream?.Close();
Console.WriteLine("文件资源已释放");
}
}
// 使用 using 确保自动释放
using (var fm = new FileManager())
{
fm.Open(@"C:\test.txt");
// 使用文件...
} // 自动调用 Dispose()IComparable 和 IEnumerable 示例
csharp
// IComparable:让对象可排序
public class Student : IComparable<Student>
{
public string Name { get; set; }
public int Score { get; set; }
public int CompareTo(Student? other)
{
if (other == null) return 1;
// 按分数排序(升序)
return Score.CompareTo(other.Score);
}
public override string ToString() => $"{Name}: {Score}";
}
// IEnumerable:让类支持 foreach
public class Classroom : IEnumerable<Student>
{
private List<Student> students = new List<Student>();
public void Add(Student student) => students.Add(student);
public IEnumerator<Student> GetEnumerator()
{
return students.GetEnumerator();
}
// 非泛型版本的实现(必须)
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
// 使用
var classroom = new Classroom();
classroom.Add(new Student { Name = "张三", Score = 85 });
classroom.Add(new Student { Name = "李四", Score = 92 });
classroom.Add(new Student { Name = "王五", Score = 78 });
// foreach 支持(因为实现了 IEnumerable)
foreach (var student in classroom)
Console.WriteLine(student);
// 排序支持(因为实现了 IComparable)
var list = classroom.ToList();
list.Sort();
Console.WriteLine("\n按分数排序后:");
foreach (var s in list)
Console.WriteLine(s);八、接口默认实现(C# 8.0+)
C# 8.0 开始,接口中可以提供方法的默认实现。
csharp
public interface ISpeak
{
void Speak(); // 抽象方法,实现类必须实现
// 默认实现方法(C# 8.0+)
public void Greet()
{
Console.WriteLine("你好!");
}
}
public class Person : ISpeak
{
public void Speak()
{
Console.WriteLine("我是一名程序员");
}
// Greet 方法不需要实现,使用接口的默认实现
}
var person = new Person();
person.Speak(); // 我是一名程序员
((ISpeak)person).Greet(); // 你好!(需要通过接口调用)九、接口与设计原则
接口隔离原则(ISP)
接口应该小而专,不应该包含实现类不需要的方法。
csharp
// ❌ 不推荐:大而全的接口
public interface IWorker
{
void Work();
void Eat();
void Sleep();
void Manage(); // 不是所有工人都需要管理
}
// ✅ 推荐:拆分为多个专用接口
public interface IWorkable { void Work(); }
public interface IEatable { void Eat(); }
public interface ISleepable { void Sleep(); }
public interface IManageable { void Manage(); }
// 普通工人只需要实现需要的接口
public class Worker : IWorkable, IEatable, ISleepable
{
public void Work() => Console.WriteLine("工作");
public void Eat() => Console.WriteLine("吃饭");
public void Sleep() => Console.WriteLine("睡觉");
}
// 经理额外实现管理接口
public class Manager : IWorkable, IEatable, ISleepable, IManageable
{
public void Work() => Console.WriteLine("工作");
public void Eat() => Console.WriteLine("吃饭");
public void Sleep() => Console.WriteLine("睡觉");
public void Manage() => Console.WriteLine("管理");
}核心知识点总结
接口特性速查
| 特性 | 说明 |
|---|---|
| 关键字 | interface |
| 命名规范 | 以 I 开头(如 IComparable) |
| 成员 | 方法、属性、事件、索引器(C# 8.0+ 可包含默认实现) |
| 字段 | 不能包含字段 |
| 构造函数 | 不能有构造函数 |
| 访问修饰符 | 成员默认 public,不能显式指定(C# 8.0+ 可指定) |
| 多实现 | 一个类可实现多个接口 |
| 继承 | 接口之间可以互相继承 |
接口 vs 抽象类选择指南
| 需求 | 选择 |
|---|---|
| 定义"是什么"(共同基础) | 抽象类 |
| 定义"能做什么"(能力合约) | 接口 |
| 需要共享代码实现 | 抽象类 |
| 需要多重继承 | 接口 |
| 预计未来会添加新成员 | 抽象类(接口加成员会破坏现有实现) |
注意事项
- 面向接口编程——依赖接口而非具体实现,提高可扩展性
- 接口一旦发布,应避免修改——添加新成员会破坏所有现有实现(可使用默认实现缓解)
- 接口越小越好——遵循接口隔离原则(ISP)
- 显式接口实现用于解决多接口方法冲突
- 常用内置接口:
IDisposable、IComparable、IEnumerable、IEquatable


