第五章:你的C#编程之旅:解密面向对象编程的“三把钥匙”

C# 面向对象编程三大特性详解

  1. 封装是基础

  2. 继承建立层次

  3. 多态实现灵活

5.1 封装(Encapsulation)

什么是封装?

想象一下,你有一个存钱罐。你可以往里面存钱,也可以取钱,但你不需要知道存钱罐内部是怎么计算余额的。这就是封装的核心思想!

封装就像是一个黑盒子:

  • 外面的人只能通过指定的"开口"(公共方法)来使用

  • 内部的复杂机制被隐藏起来

  • 使用起来更简单,也更安全

为什么要使用封装?

  1. 安全性:防止数据被意外修改

  2. 易用性:使用者不需要了解内部细节

  3. 可维护性:内部修改不影响外部使用

访问修饰符:控制谁能看到什么

在C#中,我们用访问修饰符来设置"权限级别":

class BankAccount
{
    private decimal balance;        // 只有这个类自己能看
    protected DateTime createdDate; // 这个类和它的"孩子"能看  
    internal string bankCode;       // 同一个项目里的都能看
    public string OwnerName;        // 谁都能看
}

简单理解:

  • private:我的私人日记,只有我能看

  • protected:家庭相册,我和家人能看

  • internal:公司内部文件,同事都能看

  • public:公开公告,谁都能看

实际应用:员工管理系统

让我们看一个实际的例子:

class Employee
{
    // 私有字段 - 外部不能直接访问
    private decimal _salary;

    // 公共属性 - 外部可以通过这个来访问
    public string Name { get; set; }
    
    public decimal Salary 
    {
        get { return _salary; }        // 读取薪资
        private set                     // 只能在内部修改
        { 
            if (value < 0)
                throw new Exception("薪资不能为负数!");
            _salary = value;
        }
    }

    public int Age 
    {
        get { return _age; }
        set 
        {
            if (value < 18 || value > 65)
                throw new Exception("年龄必须在18-65岁之间");
            _age = value;
        }
    }
}

使用方式:

Employee emp = new Employee();
emp.Name = "张三";        // ✅ 可以设置
emp.Age = 25;            // ✅ 可以设置
// emp.Salary = 5000;     // ❌ 会报错!不能直接设置薪资
emp.RaiseSalary(1000);   // ✅ 只能通过特定方法修改

封装的好处总结

  1. 数据验证:确保数据符合业务规则

  2. 安全性:防止不合理的数据修改

  3. 灵活性:可以随时修改内部实现而不影响外部


5.2 继承(Inheritance)

什么是继承?

继承就像"子承父业":

  • 儿子会继承父亲的特征和能力

  • 但儿子也可以有自己的特色

  • 这样可以避免重复造轮子

基本语法

// 父类(基类)
class Vehicle           // 交通工具
{
    public string Brand { get; set; }
    public void Start() 
    {
        Console.WriteLine("交通工具启动了");
    }
}

// 子类(派生类)
class Car : Vehicle     // 汽车继承自交通工具
{
    public int Doors { get; set; }
    
    public void Honk()
    {
        Console.WriteLine("滴滴!");
    }
}

继承的使用

Car myCar = new Car();
myCar.Brand = "丰田";   // 从父类继承的属性
myCar.Doors = 4;        // 自己的属性
myCar.Start();          // 从父类继承的方法
myCar.Honk();           // 自己的方法

方法重写:青出于蓝

有时候,子类想要改变父类的方法:

class Vehicle
{
    public virtual void Start()  // virtual表示可以重写
    {
        Console.WriteLine("交通工具启动了");
    }
}

class Car : Vehicle
{
    public override void Start() // override表示重写
    {
        Console.WriteLine("汽车点火启动了!");
    }
}

base关键字:引用父类

class Car : Vehicle
{
    public override void Start()
    {
        base.Start();  // 先执行父类的Start方法
        Console.WriteLine("检查油量..."); // 再执行自己的逻辑
    }
}

继承的注意事项

  1. 单一继承:一个类只能有一个父类

  2. 传递性:继承关系会传递(爷爷→爸爸→儿子)

  3. 构造顺序:先构造父类,再构造子类


5.3 多态(Polymorphism)

什么是多态?

多态的意思是"多种形态"。就像"开车"这个动作:

  • 开轿车、开卡车、开公交车都是"开车"

  • 但具体怎么开,每种车都不一样

多态的实现方式

方式一:虚方法重写

class Shape
{
    public virtual void Draw()  // 虚方法
    {
        Console.WriteLine("绘制形状");
    }
}

class Circle : Shape
{
    public override void Draw() // 重写方法
    {
        Console.WriteLine("绘制圆形");
    }
}

class Rectangle : Shape
{
    public override void Draw() // 重写方法
    {
        Console.WriteLine("绘制矩形");
    }
}

使用多态:

Shape[] shapes = new Shape[3];
shapes[0] = new Shape();
shapes[1] = new Circle();
shapes[2] = new Rectangle();

foreach (Shape shape in shapes)
{
    shape.Draw();  // 同一个方法,不同表现!
}

输出结果:

绘制形状
绘制圆形  
绘制矩形

多态的优势

  1. 代码通用性:用统一的接口处理不同类型的对象

  2. 扩展性好:添加新类型时不需要修改现有代码

  3. 可替换性:不同对象可以相互替换


5.4 抽象类与接口

抽象类:不完全的蓝图

抽象类就像建筑的设计图,但有些细节还没确定:

abstract class Animal  // 抽象类,不能直接创建对象
{
    public string Name { get; set; }
    
    // 抽象方法 - 只有定义,没有实现
    public abstract void MakeSound();
    
    // 普通方法 - 有具体实现
    public void Sleep()
    {
        Console.WriteLine($"{Name}在睡觉");
    }
}

class Dog : Animal
{
    // 必须实现抽象方法
    public override void MakeSound()
    {
        Console.WriteLine("汪汪!");
    }
}

使用:

// Animal animal = new Animal();  // ❌ 错误!不能创建抽象类对象
Animal myDog = new Dog();         // ✅ 正确!用子类创建
myDog.Name = "小白";
myDog.MakeSound();  // 输出:汪汪!
myDog.Sleep();      // 输出:小白在睡觉

接口:能力约定

接口定义了一个对象"能做什么",而不关心它"是什么":

// 定义接口
interface IFlyable
{
    void Fly();  // 只有方法签名,没有实现
}

interface ISwimmable  
{
    void Swim();
}

// 实现接口
class Duck : IFlyable, ISwimmable  // 可以实现多个接口
{
    public void Fly()
    {
        Console.WriteLine("鸭子在飞");
    }
    
    public void Swim()
    {
        Console.WriteLine("鸭子在游泳");
    }
}

抽象类 vs 接口

特性抽象类接口
定义"是什么"的关系"能做什么"的关系
方法可以有具体方法只有方法签名
继承单继承多实现
字段可以有字段不能有字段
使用场景有共同基础的类需要特定能力的类

实际选择建议

  • 抽象类:当多个类有共同的基类和共享代码时

  • 接口:当需要定义一组相关功能,或者需要多继承时


💡 学习建议

理解概念比记忆语法更重要

  1. 封装:想着"存钱罐",隐藏细节,提供接口

  2. 继承:想着"父子关系",代码复用,层次结构

  3. 多态:想着"同一操作,不同表现",提高灵活性

循序渐进的学习路径

  1. 先掌握封装 - 这是基础

  2. 再学习继承 - 理解代码复用

  3. 然后学习多态 - 提高设计能力

  4. 最后区分抽象类和接口 - 完成进阶

常见误区避免

❌ "我要用最复杂的技术" ✅ "用最适合当前需求的技术"

❌ "死记硬背语法" ✅ "理解设计思想和应用场景"

记住:面向对象编程是为了让代码更好理解、更好维护。选择合适的技术,而不是最复杂的技术!


🎯 下一步学习

掌握了这些基础概念后,你可以:

  1. 尝试设计一个小型项目

  2. 练习识别什么时候该用继承、什么时候该用接口

  3. 学习设计模式,了解这些特性的实际应用

编程就像搭积木,先理解每个积木块的用途,再学习如何组合它们构建复杂系统!

附录

教程源码已上传网盘,需要请自取

posted @ 2025-11-21 23:24  云中小生  阅读(12)  评论(0)    收藏  举报  来源