Unity基础之C#核心篇笔记3:继承

Unity基础之C#核心篇笔记3:继承

  • 继承的基本规则
    • 1.基本概念
    • 2.基本语法
    • 3.实例
    • 4.访问修饰符的影响
    • 5.子类和父类的同名成员
    • 6.总结
    • 7.练习题
  • 里氏替换原则
    • 1.基本概念
    • 2.基本实现
    • 3.is和as
    • 4.总结
    • 5.练习题
  • 继承中的构造函数
    • 1.知识回顾
    • 2.继承中的构造函数 基本概念
    • 3.继承中构造函数的执行顺序
    • 4.父类的无参构造函重要
    • 5.总结
  • 万物之父和装箱拆箱
    • 1.里氏替换知识点回顾
    • 2.万物之父
    • 3.万物之父的使用
    • 4.装箱拆箱
    • 5.总结
    • 6.练习题
  • 密封类
    • 1.基本概念
    • 2.实例
    • 3.作用
    • 4.总结
    • 5.练习题

继承的基本规则

1.基本概念

 	//一个类A继承一个类B
    //类A将会继承类B的所有成员
    //A类将拥有B类的所有特征和行为

    //被继承的类
    //称为 父类、基类、超类

    //继承的类
    //称为子类、派生类

    //子类可以有自己的特征和行为    

    //特点
    //1.单根性 子类只能有一个父类
    //2.传递性 子类可以间接继承父类的父类  

2.基本语法

    //class 类名 : 被继承的类名
    //{

    //}

3.实例

    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.访问修饰符的影响

//public - 公共 内外部访问
//private - 私有 内部访问
//protected - 保护 内部和子类访问

//之后讲命名空间的时候讲
//internal - 内部的 只有在同一个程序集的文件中,内部类型或者是成员才可以访问
//class 类名 : 被继承的类名
//{

//}

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

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

6.总结

继承基本语法
class 类名:父类名
1.单根性:只能继承一个父类
2.传递性:子类可以继承父类的父类。。。的所有内容
3.访问修饰符 对于成员的影响
4.极奇不建议使用 在子类中申明和父类同名的成员(以后学习了多态再来解决这个问题)

7.练习题

    //写一个人类,人类中有姓名,年龄属性,有说话行为
    //战士类继承人类,有攻击行为

    class Warrior : Person
    {
        public void Atk( Warrior otherWarrior )
        {
            Console.WriteLine("{0}打了{1}", Name, otherWarrior.Name);
        }
    }

    class Person
    {
        public string Name
        {
            get;
            set;
        }
        private int Age
        {
            get;
            set;
        }
        public void Speak(string str)
        {
            Age = 10;
            Console.WriteLine(Age);
            Console.WriteLine(str);
        }
    }
  

里氏替换原则

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攻击");
        }
    }
       class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("里氏替换原则");

            //里氏替换原则 用父类容器 装载子类对象
            GameObject player = new Player();
            GameObject monster = new Monster();
            GameObject boss = new Boss();

            GameObject[] objects = new GameObject[] { new Player(), new Monster(), new Boss() };


        }
    }

3.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();
                }
            }

4.总结

    //概念:父类容器装子类对象
    //作用:方便进行对象的存储和管理
    //使用:is和as
    // is 用于判断
    // as 用于转换
    // 注意:不能用子类容器装父类对象

5.练习题

1.练习题1

    //is和as的区别是什么
    // is 用于判断  类对象判断是不是某一个类型的对象   bool 
    // as 用于转换  把一个类对象 转换成一个指定类型的对象   null

2.练习题2

//写一个Monster类,它派生出Boss和Goblin两个类,
    //Boss有技能;小怪有攻击;
    //随机生成10个怪,装载到数组中
    //遍历这个数组,调用他们的攻击方法,如果是boss就释放技能
    class Monster
    {

    }

    class Boss : Monster
    {
        public void Skill()
        {
            Console.WriteLine("boss技能");
        }
    }

    class Goblin:Monster
    {
        public void Atk()
        {
            Console.WriteLine("哥布林攻击");
        }
    }
       class Program
    {
        static void Main(string[] args)
        {
            //随机生成10个怪物 
            Random r = new Random();
            int randomNum;
            Monster[] monsters = new Monster[10];
            for (int i = 0; i < monsters.Length; i++)
            {
                randomNum = r.Next(1, 101);
                if( randomNum < 50 )
                {
                    monsters[i] = new Boss();
                }
                else
                {
                    monsters[i] = new Goblin();
                }
            }
            //遍历10个怪物 执行对应方法
            for (int i = 0; i < monsters.Length; i++)
            {
                if( monsters[i] is Boss )
                {
                    (monsters[i] as Boss).Skill();
                }
                else
                {
                    (monsters[i] as Goblin).Atk();
                }
            }
        }
    }

3.练习题3

//FPS游戏模拟
    //写一个玩家类,玩家可以拥有各种武器
    //现在有四种武器,冲锋枪,散弹枪,手枪,匕首
    //玩家默认拥有匕首
    //请在玩家类中写一个方法,可以拾取不同的武器替换自己拥有的武器
    class Weapon
    {

    }
    //冲锋枪
    class SubmachineGun : Weapon
    {

    }
    //散弹枪
    class ShotGun : Weapon
    {

    }
    //手枪
    class Pistol : Weapon
    {

    }
    //匕首
    class Dagger:Weapon
    {

    }
    
    class Player
    {
        private Weapon nowHaveWeapon;

        public Player()
        {
            nowHaveWeapon = new Dagger();
        }

        public void PickUp(Weapon weapon)
        {
            nowHaveWeapon = weapon;
        }
    }
        class Program
    {
        static void Main(string[] args)
        {
            Player p = new Player();

            SubmachineGun s = new SubmachineGun();
            p.PickUp(s);

            ShotGun sg = new ShotGun();
            p.PickUp(sg);
        }
    }

继承中的构造函数

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构造");
        }
    }


    class Son:Father
    {
        #region 知识点四 通过base调用指定父类构造
        public Son(int i) : base(i)
        {
            Console.WriteLine("Son的一个参数的构造");
        }

        public Son(int i, string str):this(i)
        {
            Console.WriteLine("Son的两个参数的构造");
        }
        #endregion
    }
     class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("继承中的构造函数");

            MainPlayer mp = new MainPlayer();

            Son s = new Son(1,"123");
        }
    }

5.总结

 总结
继承中的构造函数
特点
执行顺序 是先执行父类的构造函数 再执行子类的 从老祖宗开始 依次一代一代向下执行

父类中的无参构造函数 很重要
如果被顶掉 子类中就无法默认调用无参构造了
解决方法:
1.始终保持申明一个无参构造
2.通过base关键字 调用指定父类的构造
注意:
区分this和base的区别

万物之父和装箱拆箱

1.里氏替换知识点回顾

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

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

        }
    }

2.万物之父

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

3.万物之父的使用

		static void Main(string[] args)
        {
  			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 fl = (float)o2;

            //特殊的string类型
            object str = "123123";
            string str2 = str as string;

            object arr = new int[10];
            int[] ar = arr as int[];
         }

4.装箱拆箱

     static void Main(string[] args)
     {
			//发生条件
            //用object存值类型(装箱)
            //再把object转为值类型(拆箱)

            //装箱
            //把值类型用引用类型存储
            //栈内存会迁移到堆内存中

            //拆箱
            //把引用类型存储的值类型取出来
            //堆内存会迁移到栈内存中

            //好处:不确定类型时可以方便参数的存储和传递
            //坏处:存在内存迁移,增加性能消耗

            //装箱
            object v = 3;
            //拆箱
            int intValue = (int)v;

            TestFun(1, 2, 3, 4f, 34.5, "123123", new Son());
            static void TestFun( params object[] array )
        {

        }
     }

5.总结

万物之父:object
基于里氏替换原则的 可以用object容器装载一切类型的变量
它是所有类型的基类

 装箱拆箱
 用object存值类型(装箱)
 把object里面存的值 转换出来(拆箱)
 好处
 不去定类型时可以用 方便参数存储和传递
 坏处
 存在内存的迁移 增加了性能消耗

 不是不用,尽量少用

6.练习题

1.练习题1

	请口头描述什么是装箱拆箱
 			//发生条件
            //用object存值类型(装箱)
            //在把object里存的这个值类型转为值对象时(拆箱)

            // 装箱
            // 把值类型用引用类型来存储
            // 栈内存迁移到堆内存中

            // 拆箱
            // 把引用存储的值类型取出来放入值对象中
            // 堆内存迁移到了栈内存中

            // 好处:不确定类型时可以使用 object来存储任何类型 方便存储和传递
            // 坏处:存在内存迁移,增加性能消耗

2.练习题2

请用代码描述装箱拆箱
			//装箱
            int i = 10;
            object obj = i;
            //拆箱
            i = (int)obj;

密封类

1.基本概念

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

2.实例

class Father
    {

    }

    sealed class Son:Father
    {

    }

3.作用

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

4.总结

 关键字:sealed
 作用:让类无法再被继承
 意义: 加强面向对象程序设计的 规范性、结构性、安全性

5.练习题

定义一个载具类
有速度,最大速度,可乘人数,司机和乘客等,有上车,下车,行驶,车祸等方法,
用载具类声明一个对象,并将若干人装载上车
   class Person
    {

    }

    class Driver:Person
    {

    }

    class Passenger:Person
    {

    }

    class Car
    {
        public int speed;
        public int maxSpeed;
        //当前装载了多少人
        public int num;

        public Person[] persons;

        public Car( int speed, int maxSpeed, int num )
        {
            this.speed = speed;
            this.maxSpeed = maxSpeed;
            this.num = 0;
            persons = new Person[num];
        }

        public void GetIn( Person p )
        {
            if( num >= persons.Length )
            {
                Console.WriteLine("满载");
                return;
            }
            persons[num] = p;
            ++num;
        }

        public void GetOff(Person p)
        {
            for (int i = 0; i < persons.Length; i++)
            {
                if (persons[i] == null)
                {
                    break;
                }
                if( persons[i] == p )
                {
                    //移动位置
                    for (int j = i; j < num - 1; j++)
                    {
                        persons[j] = persons[j + 1];
                    }
                    //最后一个位置清空
                    persons[num - 1] = null;
                    --num;
                    break;
                }
            }
        }

        public void Move()
        {

        }

        public void Boom()
        {

        }
    }

    #endregion
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("封装继承综合练习");

            Car c = new Car(10, 20, 20);
            Driver d = new Driver();
            c.GetIn(d);

            Passenger p = new Passenger();
            c.GetIn(p);
            Passenger p2 = new Passenger();
            c.GetIn(p2);
            Passenger p3 = new Passenger();
            c.GetIn(p3);

            c.GetOff(p2);
        }
    }

你可能感兴趣的:(Unity基础之C#核心篇笔记)