一、前言:为什么需要体系化学习 C# 面向对象?
C# 作为 .NET 生态的核心语言,其面向对象编程(OOP)特性不仅是语法基础,更是构建可维护、可扩展软件系统的设计哲学。许多开发者虽然能写出 C# 代码,但对类与对象、继承、多态、封装、抽象等核心概念的理解往往停留在“会用”层面,缺乏体系化的认知框架。
本文旨在为你提供一份从基础到进阶的 C# 面向对象通关指南,通过清晰的脉络梳理和实战代码示例,帮助你建立完整的知识体系,真正掌握面向对象编程的精髓。
二、面向对象编程四大支柱
在深入具体语法前,我们先从概念层面理解面向对象编程的四大支柱:
- 封装(Encapsulation):将数据(字段)和操作数据的方法(方法)捆绑在一起,并对外隐藏内部实现细节,仅通过公开的接口进行交互。
- 继承(Inheritance):允许创建新类(派生类)基于现有类(基类)的属性和行为,实现代码复用和层次化设计。
- 多态(Polymorphism):同一操作作用于不同的对象,可以有不同的解释和执行结果。主要通过方法重写和接口实现。
- 抽象(Abstraction):隐藏复杂的实现细节,仅向外界暴露必要的、简化的接口。
三、类与对象:面向对象的基石
3.1 类的定义与实例化
// 定义一个简单的 Person 类 public class Person { // 字段(通常私有,通过属性暴露) private string _name; private int _age; // 属性:封装字段的访问 public string Name { get { return _name; } set { _name = value; } } public int Age { get { return _age; } set { if (value >= 0) // 封装验证逻辑 _age = value; else throw new ArgumentException("年龄不能为负数"); } } // 构造函数 public Person(string name, int age) { Name = name; Age = age; } // 方法 public void Introduce() { Console.WriteLine($"你好,我是{Name},今年{Age}岁。"); } } // 实例化对象 class Program { static void Main(string[] args) { Person person1 = new Person("张三", 25); person1.Introduce(); // 输出:你好,我是张三,今年25岁。 } }3.2 访问修饰符
public:完全公开,任何地方都可访问。private:仅类内部可访问(默认)。protected:类内部和派生类可访问。internal:同一程序集内可访问。protected internal:同一程序集或派生类可访问。
四、封装:数据隐藏与安全访问
封装不仅仅是使用private字段,更重要的是通过属性、方法提供受控的访问通道。
public class BankAccount { // 私有字段,外部无法直接访问 private decimal _balance; private string _accountNumber; // 只读属性 public string AccountNumber => _accountNumber; // 带逻辑的属性 public decimal Balance { get { return _balance; } private set // 存款操作在类内部完成 { if (value >= 0) _balance = value; } } public BankAccount(string accountNumber, decimal initialBalance) { _accountNumber = accountNumber; Balance = initialBalance; } // 公开的存款方法 public void Deposit(decimal amount) { if (amount <= 0) throw new ArgumentException("存款金额必须大于0"); Balance += amount; Console.WriteLine($"成功存入 {amount:C},当前余额:{Balance:C}"); } // 公开的取款方法(带验证) public bool Withdraw(decimal amount) { if (amount <= 0) throw new ArgumentException("取款金额必须大于0"); if (amount > Balance) { Console.WriteLine("余额不足"); return false; } Balance -= amount; Console.WriteLine($"成功取出 {amount:C},当前余额:{Balance:C}"); return true; } }五、继承:代码复用与层次化设计
5.1 基本继承语法
// 基类(父类) public class Vehicle { public string Brand { get; set; } public int Speed { get; protected set; } public Vehicle(string brand) { Brand = brand; Speed = 0; } public virtual void Accelerate(int increment) { Speed += increment; Console.WriteLine($"{Brand} 加速到 {Speed} km/h"); } public void Brake() { Speed = 0; Console.WriteLine($"{Brand} 已刹车"); } } // 派生类(子类) public class Car : Vehicle { public int DoorCount { get; set; } public Car(string brand, int doorCount) : base(brand) { DoorCount = doorCount; } // 重写基类方法 public override void Accelerate(int increment) { // 调用基类实现 base.Accelerate(increment); Console.WriteLine($"(这是一辆{DoorCount}门轿车)"); } // 子类特有方法 public void Honk() { Console.WriteLine($"{Brand} 鸣笛:嘀嘀!"); } } // 使用 class Program { static void Main(string[] args) { Car myCar = new Car("Toyota", 4); myCar.Accelerate(50); // 输出:Toyota 加速到 50 km/h(这是一辆4门轿车) myCar.Honk(); // 输出:Toyota 鸣笛:嘀嘀! } }5.2 继承中的构造函数调用顺序
- 派生类构造函数首先调用基类构造函数(通过
: base(...)) - 执行基类构造函数体(通过
: sohuedu.cN(...)) - 执行派生类构造函数体
5.3sealed关键字
使用sealed修饰类可以防止该类被继承:
public sealed class FinalClass { // 这个类不能被继承 } // 错误:无法从密封类派生 // public class DerivedClass : FinalClass { }六、多态:同一接口,不同实现
6.1 方法重写(override)与虚方法(virtual)
public class Shape { public virtual void Draw() { Console.WriteLine("绘制一个形状"); } } public class Circle : Shape { public override void Draw() { Console.WriteLine("绘制一个圆形"); } } public class Rectangle : Shape { public override void Draw() { Console.WriteLine("绘制一个矩形"); } } class Program { static void Main(string[] args) { // 多态:父类引用指向子类对象 Shape shape1 = new Circle(); Shape shape2 = new Rectangle(); Shape shape3 = new Shape(); shape1.Draw(); // 输出:绘制一个圆形 shape2.Draw(); // 输出:绘制一个矩形 shape3.Draw(); // 输出:绘制一个形状 } }6.2 抽象类与抽象方法
// 抽象类:不能实例化,只能被继承 public abstract class Animal { // 抽象方法:没有实现,必须在派生类中重写 public abstract void MakeSound(); // 普通方法可以有实现 public void Sleep() { Console.WriteLine("动物正在睡觉..."); } } public class Dog : Animal { public override void MakeSound() { Console.WriteLine("汪汪!"); } } public class Cat : Animal { public override void MakeSound() { Console.WriteLine("喵喵!"); } } class Program { static void Main(string[] args) { Animal dog = new Dog(); Animal cat = new Cat(); dog.MakeSound(); // 汪汪! cat.MakeSound(); // 喵喵! // 错误:无法创建抽象类的实例 // Animal animal = new Animal(); } }七、接口:契约式编程
7.1 接口定义与实现
// 接口定义:只包含方法、属性、事件、索引器的签名 public interface ILogger { void Log(string message); void LogError(string error); } public interface IConfigurable { string Configuration { get; set; } } // 类可以实现多个接口 public class FileLogger : ILogger, IConfigurable { public string Configuration { get; set; } public void Log(string message) { Console.WriteLine($"[INFO] {DateTime.Now}: {message}"); } public void LogError(string error) { Console.WriteLine($"[ERROR] {DateTime.Now}: {error}"); } } public class DatabaseLogger : ILogger { public void Log(string message) { // 模拟写入数据库 Console.WriteLine($"写入数据库日志: {message}"); } public void LogError(string error) { Console.WriteLine($"写入数据库错误: {error}"); } } class Program { static void Main(string[] args) { ILogger logger = new FileLogger(); logger.Log("应用程序启动"); logger = new DatabaseLogger(); logger.LogError("数据库连接失败"); } }7.2 接口 vs 抽象类
| 特性 | 接口 | 抽象类 |
|---|---|---|
| 多重继承 | 支持(一个类可实现多个接口) | 不支持(一个类只能继承一个抽象类) |
| 默认实现 | C# 8.0 前不支持,8.0 后支持默认方法 | 支持 |
| 字段 | 不能包含aupuyb.cN实例字段 | 可以包含字段 |
| 构造函数 | 不能有构造函数 | 可以有构造函数 |
| 访问修饰符 | 成员默认 public | 可以有各种访问修饰符 |
| 设计目的 | 定义契约/能力 | 提供通用基类实现 |
八、进阶特性
8.1 属性与自动属性
// 完整属性(带私有字段) private string _name; public string Name { get { return _name; } set { _name = value; } } // 自动属性(编译器自动生成私有字段) public string Name { get; set; } // 只读自动属性(只能在构造函数中赋值) public string Id { get; } // 表达式体属性(C# 6.0+) public string FullName => $"{FirstName} {LastName}"; // 属性初始化器(C# 6.0+) public List<string> Tags { get; set; } = new List<string>();8.2 静态类与静态成员
// 静态类:不能实例化,只能包含静态成员 public static class MathHelper { public static double PI = 3.1415926; public static int Add(int a, int b) => a + b; public static int Max(int a, int b) => a > b ? a : b; } // 使用静态类 double area = MathHelper.PI * radius * radius; int sum = MathHelper.Add(10, 20); // 静态构造函数:在类首次使用前自动调用一次 public class Configuration { public static string ConnectionString { get; } static Configuration() { ConnectionString = "Server=localhost;Database=Test;"; Console.WriteLine("静态构造函数被调用"); } }8.3 索引器
public class StringCollection { private List<string> _items = new List<string>(); // 索引器:使对象可以像数组一样使用 public string this[int index] { get => _items[index]; set => _items[index] = value; } public void Add(string item) => _items.Add(item); public int Count => _items.Count; } class Program { static void Main(string[] args) { var collection = new StringCollection(); collection.Add("第一项"); collection.Add("第二项"); // 使用索引器访问 Console.WriteLine(collection[0]); // 输出:第一项 collection[1] = "修改后的第二项"; } }九、设计原则与最佳实践
9.1 SOLID 原则在 C# 中的体现
- 单一职责原则(SRP):一个类只负责一件事
- 开闭原则(OCP):对扩展开放,对修改关闭(使用抽象和接口)
- 里氏替换原则(LSP):派生类必须能够替换其基类
- 接口隔离原则(ISP):多个特定接口优于一个通用接口
- 依赖倒置原则(DIP):依赖抽象,不依赖具体实现
9.2 常见设计模式示例
// 工厂模式示例 public interface IShape { void Draw(); } public class Circle : IShape { public void Draw() => Console.WriteLine("绘制圆形"); } public class Rectangle : IShape { public void Draw() => Console.WriteLine("绘制矩形"); } public static class ShapeFactory { public static IShape CreateShape(string shapeType) { return shapeType.ToLower() switch { "circle" => new Circle(), "rectangle" => new Rectangle(), _ => throw new ArgumentException("不支持的形状类型") }; } } // 策略模式示例 public interface IPaymentStrategy { void Pay(decimal amount); } public class CreditCardPayment : IPaymentStrategy { public void Pay(decimal amount) => Console.WriteLine($"信用卡支付 {amount:C}"); } public class PayPalPayment : IPaymentStrategy { public void Pay(decimal amount) => Console.WriteLine($"PayPal 支付 {amount:C}"); } public class ShoppingCart { private IPaymentStrategy _paymentStrategy; public void SetPaymentStrategy(IPaymentStrategy strategy) { _paymentStrategy = strategy; } public void Checkout(decimal amount) { _paymentStrategy?.Pay(amount); } }十、实战:构建一个简单的电商系统模型
// 抽象产品类 public abstract class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } public abstract string GetDescription(); public virtual decimal CalculateDiscount(int quantity) { return quantity >= 10 ? Price * 0.1m : 0; } } // 具体产品类 public class Book : Product { public string Author { get; set; } public string ISBN { get; set; } public override string GetDescription() { return $"{Name} by {Author} (ISBN: {ISBN})"; } public override decimal CalculateDiscount(int quantity) { // 书籍的特殊折扣规则 var baseDiscount = base.CalculateDiscount(quantity); return baseDiscount + (quantity >= 5 ? Price * 0.05m : 0); } } public class Electronics : Product { public string Brand { get; set; } public string Model { get; set; } public override string GetDescription() { return $"{Brand} {Model} - {Name}"; } } // 接口:可发货 public interface IShippable { decimal CalculateShippingCost(string destination); void MarkAsShipped(); } // 订单类 public class Order { private List<Product> _products = new List<Product>(); public void AddProduct(Product product) => _products.Add(product); public decimal CalculateTotal() { return _products.Sum(p