C# 接口:interface 定义与实现
在 C# 中,interface(接口)是一种定义行为契约的机制。它规定了类必须实现哪些方法、属性、事件或索引器,但不提供具体实现。通过接口,你可以让多个不相关的类遵循同一套规则,从而提升代码的灵活性和可维护性。
定义一个接口
创建一个接口文件或在现有文件中 定义 接口,使用 interface 关键字。
- 新建 一个
.cs文件(例如IAnimal.cs)。 - 输入 以下代码:
public interface IAnimal
{
string Name { get; set; }
void MakeSound();
bool IsAlive { get; }
}
这段代码定义了一个名为 IAnimal 的公共接口,包含:
- 一个可读写的字符串属性
Name - 一个无返回值的方法
MakeSound() - 一个只读布尔属性
IsAlive
注意:接口中的成员不能有访问修饰符(如
public、private),因为它们默认就是public,也不能包含字段或构造函数。
实现接口
让一个类 实现 接口,需在类声明时使用冒号 : 后跟接口名,并 提供 所有接口成员的具体实现。
- 创建 一个新类(例如
Dog.cs)。 - 编写 如下代码:
public class Dog : IAnimal
{
public string Name { get; set; }
public bool IsAlive { get; private set; } = true;
public Dog(string name)
{
Name = name;
}
public void MakeSound()
{
Console.WriteLine($"{Name} says: Woof!");
}
}
```
这里 `Dog` 类实现了 `IAnimal` 接口:
- **提供了** `Name` 属性的完整 `get` 和 `set`
- **实现了** `MakeSound()` 方法,输出特定声音
- **实现了** `IsAlive` 为只读属性(通过 `private set` 控制写入)
> 如果遗漏任何一个接口成员,编译器会报错:“'Dog' does not implement interface member 'IAnimal.XXX'”。
---
## 多接口实现
一个类可以同时实现多个接口,只需用逗号分隔。
1. **定义** 第二个接口:
```csharp
public interface ITrainable
{
void Train();
}
```
2. **修改** `Dog` 类以同时实现两个接口:
```csharp
public class Dog : IAnimal, ITrainable
{
public string Name { get; set; }
public bool IsAlive { get; private set; } = true;
public Dog(string name)
{
Name = name;
}
public void MakeSound()
{
Console.WriteLine($"{Name} says: Woof!");
}
public void Train()
{
Console.WriteLine($"{Name} is learning to sit.");
}
}
```
现在 `Dog` 同时满足 `IAnimal` 和 `ITrainable` 的契约。
---
## 接口的用途:解耦与多态
使用接口的核心价值在于**解耦调用方与实现方**。你不需要知道对象的具体类型,只要它实现了某个接口,就能安全调用其方法。
1. **编写** 一个通用方法,接受任意 `IAnimal` 对象:
```csharp
public static void LetAnimalSpeak(IAnimal animal)
{
if (animal.IsAlive)
{
animal.MakeSound();
}
}
```
2. **在主程序中调用**:
```csharp
var dog = new Dog("Buddy");
LetAnimalSpeak(dog); // 输出: Buddy says: Woof!
```
即使将来新增 `Cat`、`Bird` 等类,只要它们实现 `IAnimal`,无需修改 `LetAnimalSpeak` 方法。
---
## 显式接口实现(处理命名冲突)
当两个接口有同名成员时,可使用**显式实现**来区分。
1. **定义** 两个含同名方法的接口:
```csharp
public interface IFlyable
{
void Move();
}
public interface IWalkable
{
void Move();
}
```
2. **创建** 一个同时实现两者的类,并显式实现:
```csharp
public class Duck : IFlyable, IWalkable
{
void IFlyable.Move()
{
Console.WriteLine("Duck is flying.");
}
void IWalkable.Move()
{
Console.WriteLine("Duck is walking.");
}
}
```
3. **调用时需通过接口类型转换**:
```csharp
var duck = new Duck();
((IFlyable)duck).Move(); // 输出: Duck is flying.
((IWalkable)duck).Move(); // 输出: Duck is walking.
```
显式实现的成员**不能通过类实例直接调用**,只能通过接口引用访问。
---
## 接口继承
接口可以继承其他接口,形成层级结构。
1. **定义** 基础接口:
```csharp
public interface ILivingBeing
{
bool IsAlive { get; }
}
```
2. **让** `IAnimal` **继承** 它:
```csharp
public interface IAnimal : ILivingBeing
{
string Name { get; set; }
void MakeSound();
}
```
现在任何实现 `IAnimal` 的类**必须同时实现** `ILivingBeing` 中的 `IsAlive` 属性。
---
## C# 8.0+ 的默认接口方法(谨慎使用)
从 C# 8.0 起,接口可以包含**默认实现**的方法。
```csharp
public interface IAnimal
{
string Name { get; set; }
bool IsAlive { get; }
void MakeSound()
{
Console.WriteLine($"{Name} makes a sound.");
}
}
此时,实现类可以不重写 MakeSound(),直接使用默认行为。但此特性应谨慎使用,因为它模糊了接口与抽象类的界限,可能破坏“接口仅定义契约”的原则。
若仍要重写,正常实现即可:
public class Cat : IAnimal
{
public string Name { get; set; }
public bool IsAlive => true;
public void MakeSound() // 覆盖默认实现
{
Console.WriteLine($"{Name} says: Meow!");
}
}
最佳实践总结
| 场景 | 推荐做法 |
|---|---|
| 定义行为契约 | 使用 interface,避免包含实现细节 |
| 多个类共享逻辑 | 优先考虑组合或抽象类,而非滥用默认接口方法 |
| 命名规范 | 接口名以大写字母 I 开头(如 IRepository) |
| 成员设计 | 只包含 public 成员,不包含字段或构造函数 |
| 版本演进 | 新增成员时尽量不破坏现有实现(C# 8+ 默认方法可缓解此问题) |
坚持“面向接口编程”原则:变量、参数、返回值尽可能使用接口类型而非具体类。这能显著提升代码的测试性和扩展性。

暂无评论,快来抢沙发吧!