C# 封装、继承与多态详解
面向对象编程有三大核心特性:封装、继承和多态,它们构成了OOP的重要基础。下面我将从理论到实践全面解析这三大特性在C#中的实现与应用。
一、封装(Encapsulation)
1. 封装的概念与实现
封装是指将数据(字段)和操作数据的方法(属性/方法)绑定在一起的技术。C#通过访问修饰符实现封装:
public class BankAccount
{
// 私有字段(封装数据)
private string _accountNumber;
private decimal _balance;
private DateTime _createdDate;
// 公开属性(控制访问)
public string AccountNumber
{
get => _accountNumber;
private set => _accountNumber = value;
}
public decimal Balance
{
get => _balance;
private set
{
if (value < 0)
throw new ArgumentException("余额不能为负");
_balance = value;
}
}
// 构造方法
public BankAccount(string accountNumber)
{
AccountNumber = accountNumber;
_createdDate = DateTime.Now;
}
// 公开方法(封装业务逻辑)
public void Deposit(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("存款金额必须大于零");
Balance += amount;
LogTransaction($"存入 {amount}");
}
// 私有方法(实现细节隐藏)
private void LogTransaction(string message)
{
Console.WriteLine($"[{DateTime.Now}] {message}");
}
}
2. 封装的五大优势
- 数据保护:防止对象数据被非法访问或修改
- 实现隐藏:隐藏类的实现细节,仅暴露必要接口
- 灵活性:内部修改不影响外部调用
- 易于维护:修改实现不需要改变接口
- 降低耦合:减少类之间的依赖
3. 访问修饰符总结
修饰符 | 类内部 | 派生类 | 同一程序集 | 其他程序集 |
---|---|---|---|---|
public | ✔ | ✔ | ✔ | ✔ |
private | ✔ | ✖ | ✖ | ✖ |
protected | ✔ | ✔ | ✖ | ✖ |
internal | ✔ | ✔ | ✔ | ✖ |
protected internal | ✔ | ✔ | ✔ | 仅派生类 |
二、继承(Inheritance)
1. 继承的基础语法
// 基类(父类)
public class Animal
{
public string Name { get; set; }
public int Age { get; set; }
public virtual void MakeSound()
{
Console.WriteLine("动物发出声音");
}
public void Eat()
{
Console.WriteLine("正在进食...");
}
}
// 派生类(子类)
public class Dog : Animal
{
public string Breed { get; set; }
// 方法重写
public override void MakeSound()
{
Console.WriteLine("汪汪汪!");
}
// 新增方法
public void Fetch()
{
Console.WriteLine($"{Name}正在捡球");
}
}
// 使用
Dog myDog = new Dog { Name = "Buddy", Breed = "金毛" };
myDog.MakeSound(); // 输出"汪汪汪!"
myDog.Eat(); // 继承自Animal的方法
2. 继承中的关键概念
- base关键字:访问基类成员
public class Cat : Animal
{
public override void MakeSound()
{
base.MakeSound(); // 先调用父类实现
Console.WriteLine("喵喵喵!");
}
}
- protected访问:允许子类访问
public class BaseClass
{
protected int _protectedValue = 10;
}
public class DerivedClass : BaseClass
{
public void AccessProtected()
{
Console.WriteLine(_protectedValue); // 可以访问
}
}
3. 继承的五个特点
- 单继承:C#只支持单继承(一个类只能继承一个父类)
- 传递性:继承关系可以传递(A→B→C)
- 复用性:子类自动获得父类的非private成员
- 扩展性:子类可以添加新成员或重写父类方法
- 构造顺序:先调用父类构造函数,再调用子类构造函数
三、多态(Polymorphism)
1. 多态的基本实现
public abstract class Shape
{
public abstract double Area(); // 抽象方法
public virtual void Draw() // 虚方法
{
Console.WriteLine("绘制形状");
}
}
public class Circle : Shape
{
public double Radius { get; set; }
public override double Area() => Math.PI * Radius * Radius;
public override void Draw()
{
Console.WriteLine("绘制圆形 ○");
}
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double Area() => Width * Height;
// 未重写Draw方法,使用父类实现
}
// 使用多态
List<Shape> shapes = new List<Shape>
{
new Circle { Radius = 5 },
new Rectangle { Width = 4, Height = 6 }
};
foreach (var shape in shapes)
{
shape.Draw();
Console.WriteLine($"面积: {shape.Area()}");
}
2. 多态的三种形式
-
方法重写(Override)
- 使用
virtual
声明可重写方法 - 使用
override
重写父类方法
- 使用
-
抽象类与抽象方法
public abstract class DatabaseConnection { public abstract void Open(); public abstract void Close(); public string ConnectionString { get; set; } } public class SqlConnection : DatabaseConnection { public override void Open() {/* SQL连接实现 */} public override void Close() {/* SQL关闭实现 */} }
-
接口实现
public interface ILogger { void Log(string message); } public class FileLogger : ILogger { public void Log(string message) {/* 写入文件 */} } public class ConsoleLogger : ILogger { public void Log(string message) { Console.WriteLine(message); } }
3. 多态的四大优势
- 代码可扩展:易于添加新类型而无需修改现有代码
- 接口统一:不同对象通过统一接口操作
- 运行时分派:根据实际对象类型调用对应方法
- 程序解耦:降低模块间的依赖关系
四、综合应用示例:图形编辑器
// 基类
public abstract class Graphic
{
public int X { get; set; }
public int Y { get; set; }
public ConsoleColor Color { get; set; }
public abstract void Draw();
public virtual void Move(int deltaX, int deltaY)
{
X += deltaX;
Y += deltaY;
}
}
// 派生类
public class Line : Graphic
{
public int EndX { get; set; }
public int EndY { get; set; }
public override void Draw()
{
Console.ForegroundColor = Color;
Console.WriteLine($"在({X},{Y})到({EndX},{EndY})绘制线段");
Console.ResetColor();
}
}
public class Circle : Graphic
{
public int Radius { get; set; }
public override void Draw()
{
Console.ForegroundColor = Color;
Console.WriteLine($"以({X},{Y})为中心绘制半径为{Radius}的圆");
Console.ResetColor();
}
}
// 使用多态
Graphic[] graphics = new Graphic[]
{
new Line { X=10, Y=10, EndX=50, EndY=50, Color=ConsoleColor.Red },
new Circle { X=30, Y=30, Radius=20, Color=ConsoleColor.Blue }
};
foreach (var g in graphics)
{
g.Draw(); // 多态调用
g.Move(5, 5); // 调用继承的方法
}
五、关键注意事项
-
何时使用继承?
- 符合"is-a"关系时才使用继承
- 当需要利用多态行为时
- 当多个类共享大量共性代码时
-
何时使用组合?
- "has-a"关系优先使用组合
public class Car { private Engine _engine; // 组合优于继承 private List<Wheel> _wheels; }
-
最佳实践
- 尽量减少继承层级(通常不超过3层)
- 优先选择sealed类防止非预期继承
- 避免使用protected字段(改用protected属性)
- 使用抽象类定义接口的默认实现
- 为多态基类考虑定义抽象方法而非虚方法
掌握封装、继承与多态这三大特性,是成为优秀C#程序员的关键。理解这些概念不仅有助于编写更优雅的代码,还能设计出更灵活、更易扩展的软件架构。