C# 语言入门(五)面向对象、类

本篇 核心知识点 :面向对象三大特性之继承、动态多态(虚方法 virtual/override)、静态多态(函数重载)、new 隐藏函数、base 关键字、抽象类 abstract、抽象方法、类构造 / 析构执行顺序、is/as 类型转换、实战动物类案例、游戏植物实战拓展

一、继承(Inheritance)

1. 核心概念

继承是面向对象三大特性(封装、继承、多态)之一,用于提取多个类的公共属性与方法,抽取为父类(基类 / Base Class),子类(派生 / Derived Class)直接复用,仅编写独有逻辑,减少重复代码,实现代码复用。

类之间语义为「is-a」:如向日葵是植物、狗是动物。

2. C# 继承硬性规则

  1. 单继承限制 :一个类只能有唯一父类,不支持 C++ 多继承、菱形继承;

  2. 可同时实现多个interface接口,弥补多继承缺失;

  3. 继承语法:class 子类 : 父类,无需额外权限修饰符;

  4. 成员继承权限规则:

    public:子类、外部全部可访问;

    protected:仅本类 + 子类可访问(继承专用修饰符);

    private:私有成员无法被子类继承,属于类内部私有资源。

3. base 关键字(核心)

概念

base专门在子类中访问父类资源,等价 Java/TSsuper,C++ 无该关键字。

三大使用场景
  1. 子类构造函数调用父类构造:子类构造(参数) : base(父参数)

  2. 子类重写方法内部,调用父类同名方法;

  3. 访问父类protected/public成员变量。

代码示例:植物父类 + 向日葵子类
复制代码
// 父类:通用植物(基类)
class Plant
{
    protected string name;
    protected int hp;
    // 带参构造
    public Plant(string n, int h)
    {
        name = n;
        hp = h;
    }
    // 通用受伤害方法(虚方法,支持重写)
    public virtual void OnHit()
    {
        Console.WriteLine($"{name}受到普通伤害");
    }
    public void PlayAnim()
    {
        Console.WriteLine("播放通用植物动画");
    }
}
​
// 子类:向日葵,继承Plant
class SunFlower : Plant
{
    // 子类构造,通过base调用父类构造
    public SunFlower(string n, int h) : base(n, h) { }
    // 子类独有方法
    public void CreateSun()
    {
        Console.WriteLine($"{name}生产阳光");
    }
}
​
// 调用测试
static void Main()
{
    SunFlower sf = new SunFlower("向日葵", 100);
    sf.PlayAnim();    // 继承父类通用方法
    sf.CreateSun();   // 子类独有
    sf.OnHit();
}

4. 构造函数继承规则

  1. 父类有无参构造时,子类实例化自动隐式调用父无参构造;

  2. 父类只有带参构造、无无参构造 ,子类必须使用:base(参数)显式调用,否则编译报错;

  3. 执行顺序:先父类构造 → 再子类构造

  4. 析构函数执行顺序相反:先子类析构 → 父类析构,析构自动调用,无法手动调用。

5. new 隐藏同名方法

概念

子类定义与父类同名、同参方法,不加override时,编译器提示隐藏警告,添加new关键字显式声明「该方法与父类无关,仅隐藏不重写」,无多态效果。

特性

仅子类变量调用该方法,父类变量指向子类对象时,仍执行父类原函数。

复制代码
class Plant
{
    public void Show() => Console.WriteLine("父类植物信息");
}
class Nut : Plant
{
    // new 隐藏父类Show,无多态
    public new void Show() => Console.WriteLine("坚果信息");
}
static void Main()
{
    Plant p = new Nut();
    p.Show(); // 输出父类方法,不会执行子类
}

二、多态(Polymorphism)

1. 分类:静态多态 + 动态多态

(1)静态多态(编译期确定)

概念:程序编译阶段就确定调用哪个函数,代表:函数重载、运算符重载

规则:同类 / 同作用域,函数名相同、参数个数 / 类型 / 顺序不同,返回值不参与重载区分。

(2)动态多态(运行时确定,本节课重点)
概念

程序运行时,根据对象真实类型自动匹配子类重写的方法,父类引用可统一管理全部子类对象(游戏引擎批量更新脚本核心原理)。

实现必备两个关键字(缺一不可)
  1. 父类方法标记virtual:标记为虚方法,允许子类重写;

  2. 子类同名方法标记override:重写覆盖父虚方法;

动态多态核心规则

1 方法名、参数列表、返回值必须完全一致;

2 父类变量接收子类实例,调用虚方法时自动执行子类override逻辑;

3 仅继承体系、虚方法可实现动态多态。

完整实战代码(坚果重写受伤害逻辑)
复制代码
class Plant
{
    protected string name;
    protected int hp;
    public Plant(string n, int h)
    {
        name = n; hp = h;
    }
    // 虚方法,允许子类重写
    public virtual void OnHit()
    {
        Console.WriteLine($"{name}普通受伤");
    }
}
​
// 坚果子类,重写受伤逻辑
class Nut : Plant
{
    public Nut(string n, int h) : base(n, h) { }
    // override 重写虚方法,实现动态多态
    public override void OnHit()
    {
        if(hp > 5000)
            Console.WriteLine($"{name}轻伤动画");
        else if(hp > 2500)
            Console.WriteLine($"{name}重伤动画");
        else
            Console.WriteLine($"{name}破碎消失");
    }
}
​
static void Main()
{
    // 父类引用指向子类,多态生效
    Plant plant = new Nut("坚果墙", 7500);
    plant.OnHit(); // 自动执行子类override方法
}

2. 多态工程价值

游戏引擎中所有脚本继承统一父类(MonoBehaviour/Component),引擎统一遍历父类数组,自动执行每个子类独有的Update()、受伤害逻辑,无需大量 if 判断类型。

三、抽象类 abstract

1. 基础概念

使用abstract修饰的类称为抽象类,用于定义一套统一规范,不能直接 new 实例化,仅能作为父类被子类继承。

2. 两大核心特性

  1. 抽象类可以包含普通成员、普通虚方法、抽象方法

  2. 抽象方法:abstract 方法签名; 只有声明、无函数体,强制非抽象子类必须override实现;

  3. 仅含抽象方法的类必须标记 abstract;抽象类可无抽象方法(仅限制实例化)。

3 硬性规则

  1. 抽象方法不能私有(private),子类无法重写;仅 public/protected;

  2. 若子类继承抽象类,但未实现全部抽象方法,该子类也必须标记 abstract;

  3. 抽象类无法创建对象,只能通过子类实例使用。

代码示例(抽象动物父类)

复制代码
// 抽象动物类,定义统一行为规范
abstract class Animal
{
    protected string name;
    public Animal(string n) { name = n; }
    // 抽象方法:无实现,子类必须重写
    public abstract void Speak();
    // 普通通用方法,所有动物复用
    public void Move()
    {
        Console.WriteLine($"{name}移动");
    }
}
​
// 狗子类,实现抽象Speak
class Dog : Animal
{
    public Dog(string n) : base(n) { }
    public override void Speak()
    {
        Console.WriteLine($"{name}:汪汪");
    }
}
​
static void Main()
{
    // Animal a = new Animal("大象"); // 报错,抽象类不能实例
    Animal dog = new Dog("旺财");
    dog.Move();
    dog.Speak(); // 执行子类实现的抽象方法
}

四、类型判断与转换 is /as

1. is 关键字

概念:

判断对象是否属于某个类 / 子类,返回 bool 布尔值。

语法:

对象 is 类型

复制代码
Plant p = new Nut("坚果",7500);
if(p is Nut)
    Console.WriteLine("该对象是坚果子类");

2. as 安全强制转换

概念:

安全类型转换,转换失败返回null,不会程序崩溃;

语法:

子类类型 变量 = 父对象 as 子类类型

对比普通强转:直接(Nut)p转换失败直接抛异常,as更安全。

复制代码
Plant p = new Nut("坚果",7500);
Nut nut = p as Nut;
if(nut != null)
    nut.OnHit();

五、构造函数 & 析构函数完整执行顺序

1 构造执行流程(从顶层父类 → 子类逐层执行)

  1. 程序执行new 子类()

  2. 优先调用最高层级父类构造

  3. 逐层向下,依次执行各级父类构造;

  4. 最后执行当前子类构造;

2 析构执行流程(销毁时,子类优先)

1 程序退出 / 对象销毁,先执行子类析构

2 逐层向上,依次调用各级父类析构;

特性

  1. 析构函数语法~类名(),无参数、无返回值、不能加访问修饰;

  2. 析构自动执行,无法手动调用;用于释放堆资源、关闭文件 / 网络;

  3. 继承体系下父类析构建议标记virtual,防止多态释放内存泄漏。

六、继承、多态综合实战

复制代码
abstract class Animal{
    protected string name;
    public Animal(string name) { this.name = name; }
    public abstract void Speak();
    public void Move() => Console.WriteLine($"{name}移动");
}
​
class Dog : Animal{
    public Dog(string n) : base(n) { }
    public override void Speak() => Console.WriteLine($"{name}:汪汪");
}
​
class Cat : Animal{
    public Cat(string n) : base(n) { }
    public override void Speak() => Console.WriteLine($"{name}:喵喵");
}
​
static void Main(){
    // 父类数组统一管理所有子类,多态批量调用
    Animal[] arr = {
        new Dog("旺财"),
        new Cat("汤姆")
    };
    foreach(var ani in arr)    {
        ani.Move();
        ani.Speak(); // 自动匹配子类实现
    }
}

七、拓展

1 C# 是否支持多继承?答:类仅单继承,可多实现接口;

2 virtual/override/new 三者区别:

virtual:父类开放重写权限;

override:子类重写虚方法,实现多态;

new:仅隐藏父方法,无运行多态;

3 base 三种使用场景:子类构造、调用父方法、访问父成员;

4 抽象类和普通类区别:抽象类不能实例、含强制实现抽象方法;

5 构造 / 析构执行顺序(笔试题高频);

6 is判断类型、as安全转换,规避强转崩溃;

7 动态多态运行时生效,静态重载编译期生效。