【C#核心】面向对象三特性之继承 (全面总结,建议收藏)

引言

继承是C#面向对象编程中的核心概念之一,它提供了一种强大的方式,允许开发者构建层次化的类结构,实现代码复用和功能扩展。然而,合理使用继承也很重要,以避免过度耦合和脆弱性问题。理解继承的力量与艺术,将使你能够更有效地应用面向对象的原则来解决实际问题。

一、继承的基本规则

1.基本概念

一个类 A 继承一个类 B
类 A 将会继承类 B 的所有成员
A 类将拥有 B 类的所有特征和行为
被继承的类
称为 父类 基类 超类
继承的类
称为 子类 派生类
子类可以有自己的特征和行为
特点
1.单根性 子类只能有一个父类
2.传递性 子类可以间接继承父类的父类

2.基本语法

class 类名 : 被继承的类名
{

}

3.实例

    class Test
    {

    }
    class Teacher
    {
        // 姓名
        public string name;
        // 职工号
        protected int number;
        // 介绍名字
        public void SpeakName()
        {
            number = 10;
            Console.WriteLine(name);
        }
    }
    class TeachingTeacher : Teacher
    {
        // 科目
        public string subject;
        // 介绍科目
        public void SpeakSubject()
        {
            number = 11;
            Console.WriteLine(subject + "老师");
        }
    }
    class ChineseTeacher : TeachingTeacher
    {
        public void Skill()
        {
            Console.WriteLine("一行白鹭上青天");
        }
    }

4.使用

            Console.WriteLine("继承的基本规则");
            TeachingTeacher tt = new TeachingTeacher();
            tt.name = "Unity打怪升级";
            // tt.number = 1;
            tt.SpeakName();
            tt.subject = "Unity";
            tt.SpeakSubject();
            ChineseTeacher ct = new ChineseTeacher();
            ct.name = "Unity";
            // ct.number = 2;
            ct.subject = "语文";
            ct.SpeakName();
            ct.SpeakSubject();
            ct.Skill();

5.访问修饰符的影响

public - 公共内外部访问
private - 私有内部访问
protected - 保护内部和子类访问
之后讲命名空间的时候讲
internal - 内部的 只有在同一个程序集的文件中 内部类型或者是成员才可以访问

6.子类和父类的同名成员

概念
C# 中允许子类存在和父类同名的成员
但是极不建议适用

二、里氏替换原则

1.基本概念

里氏替换原则是面向对象七大原则中最重要的原则
概念:
任何父类出现的地方 子类都可以代替
重点:
语法表现 – 父类容器装子类对象 因为子类对象包含了父类所有内容
作用:
方便进行对象的存储和管理

2.基本实现

    class GameObject
    {

    }
    class Player : GameObject
    {
        public void PlayerAtk()
        {
            Console.WriteLine("玩家攻击");
        }
    }
    class Monster : GameObject
    {
        public void MonsterAtk()
        {
            Console.WriteLine("怪物攻击");
        }
    }
    class Boss : GameObject
    {
        public void BossAtk()
        {
            Console.WriteLine("Boss攻击");
        }
    }

3.使用

            Console.WriteLine("里氏替换原则");
            // 里氏替换原则 用父类容器 装载子类对象
            GameObject player = new Player();
            GameObject monster = new Monster();
            GameObject boss = new Boss();
            GameObject[] objects = new GameObject[] { new Player(), new Monster(), new Boss() };

4.is 和 as

            // 基本概念
            // is:判断一个对象是否是指定类对象
            // 返回值:bool 是为真 不是为假
            // as:将一个对象转换为指定类对象
            // 返回值:指定类对象
            // 成功返回指定类对象 失败返回 null
            // 基本语法
            // 类对象 is 类名 该语句块 会有一个 bool 返回值 true 和 false
            // 类对象 as 类名 该语句块 会有一个对象返回值对象和 null
            if(player is Player)
            {
                Player p = player as Player;
                p.PlayerAtk();
                (player as Player).PlayerAtk();
            }
            for(int i = 0; i < objects.Length; i++)
            {
                if(objects[i] is Player)
                {
                    (objects[i] as Player).PlayerAtk();
                }
                else if (objects[i] is Monster)
                {
                    (objects[i] as Monster).MonsterAtk();
                }
                else if (objects[i] is Boss)
                {
                    (objects[i] as Boss).BossAtk();
                }
            }

三、继承中的构造函数

1.知识回顾

    // 构造函数
    // 实例化对象时调用的函数
    // 主要用来初始化成员变量
    // 每个类都会有一个默认的无参构造函数
    // 语法
    // 访问修饰符 类名()
    // {

    // }
    // 不写返回值
    // 函数名和类名相同
    // 访问修饰符根据需求而定 一般为 public
    // 构造函数可以重载
    // 可以用 this 语法重用代码
    // 注意
    // 有参构造会定调默认的无参构造
    // 如想保留无参构造需重载出来
    class Test
    {
        public int testI;
        public string testStr;
        public Test()
        {

        }
        public Test(int i)
        {
            this.testI = i;
        }
        public Test(int i, string str) : this(i)
        {
            this.testStr = str;
        }
    }

2.继承中的构造函数基本概念

特点
当申明一个子类对象时
先执行父类的构造函数
再执行子类的构造函数
注意:
1.父类的无参构造很重要
2.子类可以通过 base 关键字代表父类调用父类构造

3.继承中的构造函数的执行顺序

    // 父类的父类的构造-->......父类构造-->...子类构造
    class GameObject
    {
        public GameObject()
        {
            Console.WriteLine("GameObject的构造函数");
        }
    }
    class Player : GameObject
    {
        public Player()
        {
            Console.WriteLine("Player的构造函数");
        }
    }
    class MainPlayer : Player
    {
        public MainPlayer()
        {
            Console.WriteLine("MainPlayer的构造函数");
        }
    }

4.父类的无参构造函数很重要

    // 子类实例化时 默认调用的是父类的无参构造 所以如果父类无参构造被顶掉 会报错
    class Father
    {
        public Father()
        {

        }
        public Father(int i)
        {
            Console.WriteLine("Father构造");
        }
    }

4.1.通过 base 调用指定父类构造

    class Son : Father
    {
        public Son(int i) : base(i)
        {
            Console.WriteLine("Son的一个参数的构造构造");
        }
        public Son(int i,string str) : this(i)
        {
            Console.WriteLine("Son的两个参数的构造构造");
        }
    }

5.使用

            MainPlayer mp = new MainPlayer();
            Son s = new Son(1, "123");

四、万物之父和装箱拆箱

1.里氏替换知识点回顾

    // 概念:父类容器装子类对象
    // 作用:方便进行对象存储和管理
    // 使用
    // is 和 as
    // is 用于判断
    // as 用于转换
    class Father
    {

    }
    class Son : Father
    {
        public void Speak()
        {

        }
    }

2.万物之父

万物之父
关键字:object
概念:
object 是所有类型的基类 他是一个类(引用类型)
作用:
1.可以利用里氏替换原则 用 object 容器装所有对象
2.可以用来表示不确定类型 作为函数参数类型

3.万物之父的使用

            Father f = new Son();
            if (f is Son)
            {
                (f as Son).Speak();
            }
            // 引用类型
            object o = new Son();
            // 用 is as 来判断转换即可
            if (o is Son)
            {
                (o as Son).Speak();
            }
            // 值类型
            object o2 = 1f;
            // 用强转
            float f1 = (float)o2;
            // 特殊的 string 类型
            object str = "123123";
            string str2 = str as string;
            object arr = new int[10];
            int[] ar = arr as int[];

4.装箱拆箱

    // 发生条件
    // 用 object 存值类型(装箱)
    // 再把 object 转为值类型(拆箱)
    // 装箱
    // 把值类型用引用类型存储
    // 栈内存会迁移到堆内存中
    // 拆箱
    // 把引用类型存储的值类型取出来
    // 堆内存会迁移到栈内存中
    // 好处:不确定类型时可以方便参数的存储和传递
    // 坏处:存在内存迁移 增加性能消耗
    // 装箱
    object v = 3;
    // 拆箱
    int intValue = (int)v;
    TestFun(1, 2, 3, 4f, 34.5, "123123", new Son());
    #endregion
static void TestFun(params object[] array)
{

}

五、密封类

1.基本概念

密封类:是使用 sealed 密封关键字修饰的类
作用:让类无法再被继承

2.实例

    class Father
    {

    }
    sealed class Son : Father
    {

    }

3.作用

在面向对象程序的设计中 密封类的主要作用就是不允许最底层子类被继承
可以保证程序的规范性 安全性
目前对于大家来说可能用处不大
随着大家的成长 以后制作复杂系统或者程序框架时 便能慢慢体会密封的作用

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值