C# 接口 (Interface) 深度解析
接口(Interface)是 C# 面向对象编程中非常核心的概念。它定义了“行为的规范”,起到了强制契约的作用。
1. 接口简介
关键字:interface (读音: /'ɪntəfeɪs/)
接口定义了所有类继承接口时应遵循的语法合同。接口定义了“是什么”(规范),而不关心“怎么做”(实现)。
核心特性
- 纯粹的规范:接口本身通常不包含实现逻辑(C# 8.0+ 除外),它只规定了类或结构体必须具备的成员(方法、属性、事件、索引器)。
- 强制实现:一旦一个类继承了某个接口,它必须实现该接口中定义的所有成员,否则编译器会报错。
- 引用类型:接口是引用类型。虽然不能直接实例化接口(不能 `new Interface()`),但可以声明接口类型的变量,用于指向实现了该接口的类的实例(多态)。
- 访问修饰符:接口中的成员默认是 public 的。在旧版本 C# 中不允许显式添加修饰符;虽然新版本允许,但通常保持默认。
- 多继承:C# 的类只能继承一个父类(单继承),但可以实现多个接口。这是接口最强大的功能之一。
- 层级关系:接口可以继承其他接口,形成接口链。
记忆口诀:接口像是一个“职位描述(JD)”,类像是“员工”。JD 规定了员工必须会做什么(比如会编程、会英语),但具体怎么编程、怎么说英语,由员工(类)自己决定。
2. 基础语法与多态
为了实现多态(Polymorphism),我们通常使用接口类型的变量来引用子类的对象。
class Program { static void Main(string[] args) { // 多态的核心:左边是接口,右边是具体实现类 // IAnimal myDog = new IAnimal(); // 错误!接口不能被实例化 IAnimal myDog = new Dog(); // 调用接口的方法,实际执行的是 Dog 类中具体的实现 myDog.MakeSound(); // 输出 "Woof!" } } // 1. 定义接口 (通常以 I 开头命名) interface IAnimal { void MakeSound(); // 只有声明,没有方法体 } // 2. 实现接口 class Dog : IAnimal { // 必须实现接口中的所有方法,且必须是 public public void MakeSound() { Console.WriteLine("Woof! Im a Dog!"); } }
3. 接口成员:属性与方法
接口不仅可以定义方法,还可以定义属性。
注意:在你提供的示例中,`Main` 函数尝试给属性赋值,但接口定义中只有 `get`。为了让代码能运行,下面的示例修正了 `set` 访问器。
class Program { static void Main(string[] args) { // 实例化实现类 ClassGetInformation syong = new ClassGetInformation(); // 使用属性 syong.Name = "智能手机"; syong.Code = "00254"; // 调用方法 syong.ShowInfo(); } } // 定义接口 interface IInformation { // 接口中的属性通常指明需要 get 还是 set string Code { get; set; } string Name { get; set; } void ShowInfo(); } // 自定义类实现接口 public class ClassGetInformation : IInformation { // 私有字段 private string _code = ""; private string _name = ""; // 实现接口属性 Code public string Code { get { return _code; } set { _code = value; } } // 实现接口属性 Name public string Name { get { return _name; } set { _name = value; } } // 实现接口方法 public void ShowInfo() { Console.WriteLine($"设备名称: {Name}, 编号: {Code}"); } }
4. 接口的多态性 (进阶举例)
这是接口最常见的应用场景:统一管理不同的对象。
using System; class Program { static void Main(string[] args) { // 使用接口数组或集合,可以同时管理 Dog 和 Cat IAnimal[] animals = new IAnimal[2]; animals[0] = new Dog(); animals[1] = new Cat(); // 遍历集合,虽然都是 IAnimal 类型,但表现出不同的行为 foreach (var animal in animals) { animal.MakeSound(); } // 输出: // I am a Dog! // I am a Cat! } } interface IAnimal { void MakeSound(); } class Dog : IAnimal { public void MakeSound() { Console.WriteLine("I am a Dog!"); } } class Cat : IAnimal { public void MakeSound() { Console.WriteLine("I am a Cat!"); } }
5. 接口 vs 抽象类
虽然它们都不能实例化,且都被用于被继承,但它们的设计目的不同。
| 特性 | 接口 (Interface) | 抽象类 (Abstract Class) |
|---|---|---|
| 继承数量 | 多继承 (一个类可实现多个接口) | 单继承 (一个类只能继承一个抽象类) |
| 成员实现 | 主要是规范,通常无代码实现 (C# 8.0前) | 可以包含已实现的具体方法,也可以包含抽象方法 |
| 字段 (Fields) | 不能包含字段 (变量) | 可以包含字段、常量、静态成员 |
| 访问修饰符 | 默认为 public | 可以是 public, protected, private 等 |
| 设计理念 | “Can Do” (行为):像是一个插件,赋予类某种能力 (如 IDisposable, IEnumerable) | “Is A” (本质):定义类的族谱,提取子类的共性 |
6. 知识拓展:密封类 (Sealed Class)
在面向对象设计中,如果说接口是为了被继承而生,那么密封类就是禁止被继承。
关键字:sealed
为什么要用密封类?
- 安全性:防止其他开发者随意继承你的类并重写关键功能,导致逻辑破坏。
- 性能优化:编译器知道该类不会有子类,可以进行特定的底层优化(如把虚方法调用转换为直接调用)。
语法示例
// 定义一个密封类 sealed class FinalClass { public int Add(int a, int b) { return a + b; } } // 错误!无法从密封类继承 // class TryToInherit : FinalClass // { // } class Program { static void Main() { FinalClass fc = new FinalClass(); // 密封类可以被实例化 Console.WriteLine(fc.Add(1, 2)); } }
注意:
- 接口 不能 是 sealed 的(因为接口必须被继承才有意义)。
- 抽象类 不能 是 sealed 的(因为抽象类必须被继承才能实现其抽象方法)。
评论