在C#中,封装、继承、抽象、接口是面向对象编程(OOP)的四大核心特性,它们共同支撑了代码的复用性、扩展性和可维护性。下面分别详解:
一、封装(Encapsulation)
定义:将类的内部数据(字段)和操作数据的方法捆绑在一起,隐藏内部实现细节,只通过公开的接口(属性、方法)与外部交互。
核心目的:保护数据不被随意修改,确保数据的合法性;降低外部代码对类内部实现的依赖。
实现方式:通过访问修饰符控制成员的可见性:
private:仅类内部可访问(核心数据通常设为private);public:外部可直接访问(公开的接口);protected:类内部及派生类可访问;internal:同一程序集内可访问。
示例:
通过“私有字段+公开属性”实现封装,确保年龄(_age)只能被合法赋值(非负数):
public class Person
{// 私有字段:隐藏内部数据private int _age;// 公开属性:提供访问接口,附加验证逻辑public int Age{get { return _age; } // 读取set { if (value >= 0) // 验证:年龄不能为负数_age = value; else throw new ArgumentException("年龄不能为负数");}}// 公开方法:外部可调用的操作public void SayHello(){Console.WriteLine($"我今年{_age}岁");}
}// 使用
var person = new Person();
person.Age = 20; // 合法赋值
person.SayHello(); // 输出:我今年20岁
// person.Age = -5; // 抛出异常(被封装逻辑拦截)
二、继承(Inheritance)
定义:允许一个类(派生类/子类)继承另一个类(基类/父类)的成员(字段、方法等),并可扩展或重写基类功能。
核心目的:实现代码复用(避免重复编写相同逻辑);建立类的层次关系(如“动物→狗→金毛”)。
C#继承规则:
- 单继承:一个类只能直接继承一个基类(但可间接继承多层);
- 基类成员访问:派生类可访问基类的
public/protected成员,private成员不可访问; - 重写:基类用
virtual声明可重写方法,派生类用override重写。
示例:
基类Animal定义通用行为,派生类Dog和Cat继承并扩展:
// 基类
public class Animal
{public string Name { get; set; }// 虚方法:允许派生类重写public virtual void MakeSound(){Console.WriteLine("动物发出声音");}
}// 派生类Dog(继承Animal)
public class Dog : Animal
{// 重写基类方法public override void MakeSound(){Console.WriteLine($"{Name}汪汪叫");}// 扩展基类:新增子类特有方法public void Fetch(){Console.WriteLine($"{Name}叼球");}
}// 派生类Cat
public class Cat : Animal
{public override void MakeSound(){Console.WriteLine($"{Name}喵喵叫");}
}// 使用
var dog = new Dog { Name = "旺财" };
dog.MakeSound(); // 输出:旺财汪汪叫(重写的方法)
dog.Fetch(); // 输出:旺财叼球(子类特有方法)var cat = new Cat { Name = "咪咪" };
cat.MakeSound(); // 输出:咪咪喵喵叫
三、抽象(Abstraction)
定义:通过抽象类(abstract class)提取多个类的共同特征,定义“必须实现的行为”,但不提供具体实现。
核心目的:强制派生类遵循统一的接口;隐藏复杂逻辑的细节,只暴露必要的骨架。
抽象类特点:
- 不能实例化(无法
new),只能作为基类被继承; - 可包含抽象方法(
abstract修饰,无实现体,派生类必须用override实现); - 可包含具体方法(有实现体,派生类可直接使用或重写)。
示例:
抽象类Shape定义“计算面积”的抽象行为,派生类Circle和Rectangle必须实现:
// 抽象类:定义共同接口
public abstract class Shape
{// 抽象方法:无实现,强制派生类实现public abstract double CalculateArea();// 具体方法:提供通用实现public void PrintArea(){Console.WriteLine($"面积:{CalculateArea()}");}
}// 派生类:必须实现抽象方法
public class Circle : Shape
{public double Radius { get; set; }public override double CalculateArea(){return Math.PI * Radius * Radius; // 圆面积公式}
}public class Rectangle : Shape
{public double Width { get; set; }public double Height { get; set; }public override double CalculateArea(){return Width * Height; // 矩形面积公式}
}// 使用
Shape circle = new Circle { Radius = 2 };
circle.PrintArea(); // 输出:面积:12.566...(调用具体方法,内部触发重写的抽象方法)Shape rectangle = new Rectangle { Width = 3, Height = 4 };
rectangle.PrintArea(); // 输出:面积:12
四、接口(Interface)
定义:一种完全抽象的“契约”,仅声明方法、属性、事件等的签名(无任何实现),实现接口的类必须严格遵守契约(实现所有成员)。
核心目的:定义“能力”或“行为”(如“可飞行”“可序列化”);突破C#单继承限制(一个类可实现多个接口)。
接口特点:
- 关键字
interface声明,成员默认public(无需显式修饰); - 不能包含字段(只能有属性、方法等),且所有成员无实现;
- 类通过
:实现接口,必须实现接口的所有成员; - 支持多实现(一个类可实现多个接口,用
,分隔)。
示例:
接口IFly定义“飞行”能力,Bird和Plane类实现该接口:
// 接口:定义“飞行”契约
public interface IFly
{void Fly(); // 仅声明,无实现int MaxHeight { get; } // 属性签名
}// 鸟类实现“飞行”接口
public class Bird : IFly
{public void Fly(){Console.WriteLine("鸟扇动翅膀飞行");}public int MaxHeight => 1000; // 实现属性
}// 飞机类实现“飞行”接口
public class Plane : IFly
{public void Fly(){Console.WriteLine("飞机靠引擎飞行");}public int MaxHeight => 10000; // 实现属性
}// 使用
IFly bird = new Bird();
bird.Fly(); // 输出:鸟扇动翅膀飞行
Console.WriteLine($"鸟类最大飞行高度:{bird.MaxHeight}米"); // 1000米IFly plane = new Plane();
plane.Fly(); // 输出:飞机靠引擎飞行
Console.WriteLine($"飞机最大飞行高度:{plane.MaxHeight}米"); // 10000米
抽象类 vs 接口
| 区别 | 抽象类(abstract class) | 接口(interface) |
|---|---|---|
| 实现 | 可包含具体方法(有实现) | 所有成员无实现(仅声明) |
| 继承 | 单继承(一个类只能继承一个抽象类) | 多实现(一个类可实现多个接口) |
| 关系 | 表示“is-a”(是一种)关系(如“狗是动物”) | 表示“has-a”(有某种能力)关系(如“鸟会飞”) |
| 字段 | 可包含字段 | 不能包含字段(只能有属性签名) |
总结
- 封装:隐藏细节,保护数据(“怎么实现的不用管,用我的接口就行”);
- 继承:复用代码,建立层次(“父类有的我都有,我还能扩展”);
- 抽象:定义共性,强制实现(“你们都得按我规定的方法做事,但怎么做自己定”);
- 接口:定义能力,支持多实现(“谁都可以具备这个能力,只要按规则实现”)。
这四大特性结合使用,可构建出灵活、易维护的面向对象程序。