C#核心

131 阅读17分钟

 1、类和变量介绍

****namespace 类和对象
{
    #region 类声明的语法
    class Test1
    {**
        
    }
    #endregion

    internal class Program
    {
        static void Main(string[] args)
        {
            #region 基础概念
            //类是用来描述一类事物的抽象模版
            //声明地点:一般声明在namespace中
            #endregion
            #region 实例化对象
            //类的声明和类对象的声明是两个不同概念,类的声明是声明模版,对象是实例化对象。
            //类名 变量名;
            **Test1 test1;//在栈上分配了空间。
            //类名** 变量名 = null;
            Test1 test2 = null;//同上,都是栈上分配了空间,堆上没有。
            //类名 变量名 = new 类名();
            Test1 test3 = new Te**st1();//分配了空间
            #endregion**
            Test1 test4 = new Test1();
            Test1 test5 = test4;
            test5 = null;**
        }
    }
}

2、成员变量和访问修饰符

namespace 成员变量和访问修饰符
{
    enum E_Sex
    {
        man,
        woman,
    }
    #region 成员变量
    class Person
    {
        //成员变量用来描述特征
        string name = "半夜微笑狗";
        int age;
        E_Sex sex;
        //不能在class中实例化自己类型的成员变量。
        Person grilFriend;
        Person[] friend;
    }
    #endregion

    #region 访问修饰符
    //private默认修饰符,自己内部才允许调用
    //protected 受保护的,允许自己和子类访问
    //public 公共的,自己和外部都可以访问
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            #region 成员变量的默认值

            #endregion
            //打印默认值,引用类型默认是null
            Console.WriteLine(default(int)); //默认是0
            Console.WriteLine(default(bool));//默认是false
            Console.WriteLine(default(string));//null
            Console.WriteLine(default(char));//默认是null
            Console.WriteLine(default(Person));//默认是null
        }
    }
}

3、成员方法

namespace 成员方法
{
    //成员方法用来描述类的行为
    class Student
    {
        public string name;
        public int age;
        //成员方法不要加static
        public void speak(string str)
        {
            
            Console.WriteLine("{0}说{1}", name, str);
        }

        public bool isAdult()
        {
            return age >= 18;
        }
    }


    internal class Program
    {
        static void Main(string[] args)
        {
            Student s1 = new Student();
            s1.name = "李海";
            s1.speak("你好");
            s1.age = 19;
            if (s1.isAdult())
            {
                Console.WriteLine("可以谈恋爱了");
            }
        }
    }
}

4、构造、析构函数、垃圾回收


namespace 析构和垃圾回收
{
    #region 构造函数
    class Person
    {
        public string name;
        public int age;
        //构造函数用于初始化,实例化对象时会调用,没有构造函数会默认有一个无参构造函数,有参构造函数会顶替掉无参构造函数。
        //规则:1、没有返回值 2、与类名相同 3、一般用public 
        //构造函数:类允许自己声明无参,结构体并不允许
        public Person()
        {
            name = "张未";
            age = 15;
        }

        public Person(string name, int age):this(name)//加了:this()表示先调用this其他的构造函数(根据参数来判定是哪个),比如此时是1个参数为name的构造函数。然后再执行自己的构造函数。
        {
            Console.WriteLine("2个参数的");
            this.name = name;
            this.age = age;
        }

        public Person(string name)
        {
            this.name = name;
        }
    }
#endregion
    #region 析构函数
    class Person
    {
        ~Person()//在工具-选项中选择等宽字体,并且析构函数名与类名相同。
        {
            GC.Collect();
        }
    }
    #endregion

    #region 垃圾回收机制
    //GC(垃圾回收机制)只负责堆(heap)上的,引用类型的分配和回收都是通过垃圾回收机制进行的。
    //栈上的内存是由系统自动管理的。
    //0代 1代 2代
    //代是垃圾回收机制中的一种算法(分代算法)
    //每次分配都从0代开始,0代满了就会触发垃圾回收机制,清理不被引用的对象,之后将0代搬到1代中并搬迁地址连续并修改地址。
    //大对象(85000字节(83kb))通常认为是2代,以便提升性能。

    //手动触发GC
    //GC.collect();loading过场景时使用。
    #endregion

    internal class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

5、成员属性

namespace 成员属性
{
    #region 成员属性
    class Person
    {
        private int money;
        //get和set方法默认使用属性的修饰符。不能让get和set的访问权限都低于属性,加的访问修饰符要低于属性的修饰符,当然也不能重复。
        //get和set方法可以只有一个。
        //可以不声明成员变量,直接使用属性,只有get和set,用于外部能得不能改。
        public int Money
        {
            get { return money + 101; }
            set
            {
                Console.WriteLine("set方法已执行");
                //可以进行加密操作
                money = value - 101;//用value表示传入值。
            }
        }
    }
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            Person person = new Person();
            person.Money = 1;
            Console.WriteLine(person.Money);
        }
    }
}

6、索引器

namespace 索引器
{
    #region 索引器
    class Person
    {
        ////private int[,] data;
        //public int this[int i, int j]
        //{
        //    get
        //    {
        //        return data[i, j];
        //    }
        //    set
        //    {
        //        data[i, j] = value;
        //    }
        //}
        private int[] arr = new int[10];

        //索引器可以让我们访问自定义类中的元素,适合访问数组(记得初始化)可以重载。
        public int this[int index]
        {
            get { return arr[index]; }
            set { arr[index] = value; }
        }
    }
    #endregion

    internal class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person();
            //p[0, 0] = 1;
            //Console.WriteLine(p[0, 0]);
            p[0] = 1;
            Console.WriteLine(p[0]);
        }
    }
}

6、静态成员

namespace 静态成员
{
    class Person
    {
        int sum;
        Program p = new Program();
        public static void f1()
        {
            //静态成员方法使用非静态成员需要进行实例化。
            Person p = new Person();
            Console.WriteLine(p.sum);
        }
        
    }

    internal class Program
    {
        int y = 1;
        static void Main(string[] args)
        {
            Person p = new Person();
            Person.f1();
        }
    }
    //总结:
    //使用static的成员变量、方法、属性等成为静态成员
    //使用时用类名.静态成员名使用。
    //生命周期:和程序同生共死。
    //注意:静态不能直接使用非静态成员,而非静态成员可以直接使用静态成员。
}

7、静态类

namespace 静态类
{
    #region 静态类
    //概念:使用static关键字修饰的类。来将常用的静态成员写在其中,方便管理。
    //只能包含静态成员,不能被实例化。来保证工具类的唯一性。
    #endregion

    #region 静态构造函数
    //特点:1、静态类和普通类都可以有。 2、没有修饰符。 3、不能有参数。 4、只会自动调用一次(第一次使用)。
    #endregion

    #region 练习题
    static class Calculator
    {
        static float p = 3.14f;

        public static float GetRoundArea(float r)
        {
            return p * r * r;
        }

        public static float GetRoundCircumference(float r)
        {
            return 2 * p * r;
        }

        public static float GetRectArea(float a, float b)
        {
            return a * b;
        }

        public static float GetRectCircumference(float a, float b)
        {
            return a + b;
        }

        public static float GetModulus(float x)
        {
            return Math.Abs(x);
        }
    }
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            int x = (int)Calculator.GetModulus(3);
            Console.WriteLine(x);
        }
    }
}

8、扩展方法

namespace 拓展方法
{
    #region 拓展方法
    //为现有非静态变量类型 添加 方法
    //作用:提升程序的拓展性,比如不需要再在对象中重新写方法,不需要继承来重写方法,给别人封装的类型添加方法。

    //基本语法:访问修饰符 static 返回值 函数名(this 拓展类名 参数名, 类型名 参数名, ...)

    //特点:静态类中的静态方法,第一个参数 代表拓展的目标,第一个参数前面加this。
    #endregion

    #region 练习
    //扩展整形平方
    static class MyClass
    {
        public static int GetSquare(this int x)
        {
            return x * x;
        }

        public static void suicide(this Player player)
        {
            float hp = 1f;
            while(hp > 0)
            {
                hp = player.TakeDamage(player.Attack());
                Console.WriteLine("自杀还剩" + hp + "点血量");
            }
            Console.WriteLine("该角色已死亡");
        }

    }

    //给玩家类添加自杀方法
    class Player
    {
        string name;
        float hp;
        float atk;
        float def;

        public Player(string name, float hp, float atk, float def)
        {
            this.name = name;
            this.hp = hp;
            this.atk = atk;
            this.def = def;
        }

        public float Attack()
        {
            return this.atk;
        }

        public void Move()
        {
            Console.WriteLine("移动一格");
        }

        public float TakeDamage(float atk)
        {
            hp = hp - atk + def;
            if (hp < 0) hp = 0;
            Console.WriteLine("剩余:{0} 点血量。", hp);
            return hp;
        }
    }

    #endregion

    internal class Program
    {
        static void Main(string[] args)
        {
            Player p1 = new Player("巨斧战士", 12, 5, 2);
            //Player p2 = new Player("精英剑士", 10, 4, 4);
            //p1.TakeDamage(p2.Attack());
            //p1.TakeDamage(p2.Attack());
            p1.suicide();
        }
    }
}

9、运算符重载

using System.Runtime.InteropServices;

namespace 运算符重载
{
    #region 基础
    //基础语法:public static 返回类型 operator 运算符(参数列表)
    //作用:让类或结构体对象能进行运算。
    //注意:条件运算符需要配对出现。
    //      一个符号可以进行多次重载。
    //不可重载运算符:逻辑与 &&, 逻辑或 ||, 索引符[], 强转符(), 点., 三目运算符? :, 赋值符号= 
    #endregion

    #region 练习
    //定义结构体,坐标相等才相等
    struct Point
    {
        public int x;
        public int y;

        public static bool operator ==(Point p1, Point p2)
        {
            if(p1.x == p2.x && p1.y == p2.y)
            {
                return true;
            }
            return false;
        }

        public static bool operator !=(Point p1, Point p2)
        {
            if (p1.x != p2.x || p1.y != p2.y)
            {
                return false;
            }
            return true;
        }
    }
    //定义向量类实现运算
    class Vector3
    {
        public int x;
        public int y;
        public int z;

        public Vector3(int x, int y, int z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public Vector3() { }

        public static Vector3 operator +(Vector3 v1, Vector3 v2)
        {
            Vector3 v3 = new Vector3();
            v3.x = v1.x + v2.x;
            v3.y = v1.y + v2.y;
            v3.z = v1.z + v2.z;
            return v3;
        }

        public static Vector3 operator -(Vector3 v1, Vector3 v2)
        {
            Vector3 v3 = new Vector3();
            v3.x = v1.x - v2.x;
            v3.y = v1.y - v3.y;
            v3.z = v1.z - v3.z;
            return v3;
        }

        public static Vector3 operator *(Vector3 v1, int num)
        {
            Vector3 v3 = new Vector3();
            v3.x = v1.x * num;
            v3.y = v1.y * num;
            v3.z = v1.z * num;
            return v3;
        }

        public override string ToString()
        {
            return $"{x}, {y}, {z}";
        }
    }
    #endregion
    



    internal class Program
    {
        static void Main(string[] args)
        {
            Point p1 = new Point();
            Point p2 = new Point();
            p1.x = 1;
            p1.y = 1;
            p2.x = 1;
            p2.y = 1;
            Console.WriteLine(p1.x == p2.y);
            Console.WriteLine("---------------");
            Vector3 v1 = new Vector3(1, 2, 3);
            Vector3 v2 = new Vector3(3, 2, 1);
            Vector3 v3 = v1 + v2;
            Console.WriteLine(v3);
        }
    }
}

10、内部类、分部类

namespace 内部类
{
    #region 内部类基础
    //内部类使用需要点出外层。
    #endregion
    #region 分部类基础
    //把一个类分成多个部分。
    //使用partial关键字。
    //作用:扩展程序可以多个文件脚本。
    //注意:访问修饰符一致。 不能有重复成员。

    //分部方法:1、实现和声明分开。 2、不能加访问修饰符,默认私有。 3、只能在分部类中。 4、返回值是void。 5、可以有参数但不能用out关键字。
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

11、继承基本规则

using System.Xml.Linq;

namespace 继承基础
{
    #region 继承基础
    //被继承的类叫基类、父类、超类。
    //继承的类叫子类、派生类。将拥有父类的所有成员和方法、特征。
    //特点:1、单根性:子类只能有一个父类。 2、传递性:子类能间接继承父类的父类。
    /*格式:
     * class 类名 : 父类名
     * {
     * }
     */
    //子类可以声明相同成员名称来覆盖父类成员,但不推荐。可使用new关键字 public new string str;
    #endregion

    #region 练习
    class Person
    {
        protected string name;
        protected int age;

        protected void Speak()
        {
            Console.WriteLine($"我叫{name}, 我今年{age}岁了");
        }
    }

    class Soldier : Person
    {
        public void SetProfile(string name, int age)
        {
            this.name = name;  // 子类内部可访问
            this.age = age;
        }

        public string Name
        {
            get { return name; }
            set
            {
                this.name = value;
            }
        }

        public int Age
        {
            get { return age; }
            set
            {
                this.age = value;
            }
        }

        public void Attck()
        {
            Speak();
            Console.WriteLine("进攻");
        }
    }
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            Soldier sd = new Soldier();
            sd.Name = "123";
            sd.Age = 12;
            sd.Attck();
        }
    }
}

12、里氏替换原则

namespace 里氏替换
{
    #region 基础概念
    //面相对象七大原则之一。
    //概念:父类出现的地方都可以使用子类替换。
    //语法表现:父类容器装子类对象。
    //作用:方便进行对象管理和存储。
    //注意:不能用子类容器装父类对象。
    #endregion

    #region is和as
    //类对象 is 类型  返回一个bool true或false
    //类对象 as 类型  转换一个类型 指定类型或null
    #endregion

    #region 练习
    class Monster
    {

    }
    class Boss : Monster
    {
        public void Skill()
        {
            Console.WriteLine("巨力猛击");
        }
    }
    class Goblin : Monster
    {
        public void Attack()
        {
            Console.WriteLine("普通攻击");
        }
    }
    #endregion
    internal class Program
    {
        static Monster GetMonster()
        {
            Random r = new Random();
            int x = r.Next(0, 2);
            Monster monster;
            switch (x)
            {
                case 0:
                    monster = new Boss();
                    break;
                case 1:
                    monster = new Goblin();
                    break;
                default:
                    throw new InvalidOperationException("Unexpected random value");
            }
            return monster;
        }
        static void Main(string[] args)
        {
            Monster[] monster = new Monster[10];
            for(int i = 0; i < 10; i++)
            {
                monster[i] = GetMonster();
            }
            for (int i = 0; i < 10; i++)
            {
                if (monster[i] is Boss)
                {
                    (monster[i] as Boss).Skill();
                }
                else
                {
                    (monster[i] as Goblin).Attack();
                }
            }
        }
    }
}

13、继承中的构造函数

using System.Threading.Tasks.Dataflow;

namespace 继承构造函数
{
    internal class Program
    {
        #region 基础概念
        //声明子类对象,先执行父类构造,后子类构造,父类的父类会更早执行。
        //使用base关键字可以使用父类
        //父类的无参构造很重要,子类实例化会默认执行父类无参构造函数。
        //如果子类默认调用的父类构造(无参构造)被顶掉了。那么可以:public 子类名(参数名) : base(参数名){}
        #endregion

        #region 练习
        //工人基类,程序、策划、美术继承。实力化他们。
        class Worker
        {
            protected string job;
            protected string content;
            public Worker(string job, string content)
            {
                this.job = job;
                this.content = content;
                Console.WriteLine($"1工人叫{this.job}, 工作内容是{this.content}");
            }
            public void Work()
            {
                Console.WriteLine($"我的工作是{job},工作内容是{content}");
            }
        }

        class Programmer : Worker
        {
            public Programmer() : base("123", "555")
            {
                this.job = job;
                this.content = content;
                Console.WriteLine($"2工人叫{base.job}, 工作内容是{base.content}");
            }
        }
        #endregion
        static void Main(string[] args)
        {
            //Worker w = new Worker("程序", "编写代码");
            //w.Work();
            Programmer p = new Programmer();

        }
    }
}

14、万物之父和装箱、拆箱

namespace 万物之父
{
    #region 基础概念
    //关键字:object
    //概念:他是所有类型的基类
    //作用:利用里氏替换原则,用来装所有类型。
    //      可以表示不确定类型,作为参数类型。

    //使用时,引用类型可以用as,值类型用()强转。

    //装箱:引用类型存储值类型,栈内存会迁移到堆内存中。
    //拆箱:把引用类型存储的值取出,堆内存迁移到栈内存中。
    //好处:不确定类型也能存储和传递;坏处:内存迁移会消耗性能。
    #endregion

    #region 实例
    class Father
    {
        
    }
    class Son : Father;

    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            Object son = new Son();
            Console.WriteLine(son is Object);

            //装箱、拆箱
            object o = 4;
            int x = (int)o;
        }
    }
}

15、封装类

namespace 密封类
{
    #region 基础概念
    //关键字:sealed
    //作用:让类无法被继承
    //意义:提升面相对象程序的安全性、规范性、结构性。
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

16、多态

using System.Security.Cryptography;

namespace 多态
{
    #region 基础
    //多态:让同类型对象执行同一方法有不同的表现。
    //解决:让同一对象有唯一行为特征。不用转换类型。
    //vob:v:virtual 虚函数,用在父类方法上。 o:override 重写,用在与虚函数对应的子类方法上。b:base 父类,用来保留base.方法名保留父类方法。
    #endregion

    #region 练习
    //三种不同鸭子的叫声
    class Duck
    {
        public virtual void Cry()
        {
            Console.WriteLine("嘎嘎");
        }
    }
    class WoodDuck : Duck
    {
        public override void Cry()
        {
            Console.WriteLine("吱吱");
        }
    }
    class RubberDuck : Duck
    {
        public override void Cry()
        {
            Console.WriteLine("唧唧");
        }
    }
    //不同员工上班打卡
    class Staff
    {
        public virtual void ClockIn()
        {
            Console.WriteLine("9点");
        }
    }
    class Manager : Staff
    {
        public override void ClockIn()
        {
            Console.WriteLine("11点");
        }
    }
    class Programmer : Staff
    {
        public override void ClockIn()
        {
            Console.WriteLine("不用打卡");
        }
    }
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            Duck d1 = new WoodDuck();
            d1.Cry();//>>吱吱
            Duck d2 = new RubberDuck();
            d2.Cry();//>>唧唧

            Staff s1 = new Staff();
            s1.ClockIn();//>>9点
            Staff s2 = new Manager();
            s2.ClockIn();//>>11点
            Staff s3 = new Programmer();
            s3.ClockIn();//>>不用打卡
        }
    }
}

17、抽象类和抽象方法

namespace 抽象类
{
    #region 抽象类基础
    //概念:被关键字abstract修饰的类
    //特点:1、不能实例化。2、可有抽象方法。3、继承抽象类必须重写抽象方法。
    #endregion
    #region 抽象方法基础
    //概念:abstract修饰的方法
    //特点:1、没有方法体。 2、不能是私有的。 3、继承后必须override重写。 4、只能在抽象类中。
    #endregion
    #region 练习
    //证明继承的子类必须重写,孙子类也可以重写但子类必须先重写。
    abstract class Fruit
    {
        public abstract void eat();
    }
    class Apple : Fruit
    {
        public override void eat()
        {
            Console.WriteLine("1");
        }
    }
    class SupperApple : Apple
    {
        public override void eat()
        {
            base.eat();
            Console.WriteLine("1");
        }
    }
    //动物抽象类实现
    abstract class Animal
    {
        abstract public void Cry();
    }
    class Person : Animal
    {
        public override void Cry()
        {
            Console.WriteLine("人叫");
        }
    }
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            Animal animal = new Person();
            animal.Cry();
        }
    }
}

18、接口

namespace 接口
{
    #region 接口的概念
    //接口是行为的规范,接口继承可以看做是对某一类行为的抽象,两个无关系的类也可能会继承同一接口。
    //接口是一种自定义类型。
    //使用关键字interface

    //接口声明规范:
    //1、不包含成员变量,只有属性、方法、索引器、事件。
    //2、成员不能被实现,注意在C#8.0后可以实现方法,且子类不用实现,子类若重写则不用写Virtual加Override也会按照子类执行。
    //3、成员默认是public修饰符,不能是private。
    //4、接口不能继承类,但能继承另一个接口。

    //特点:
    //类可以继承多个接口。
    //继承接口后必须实现方法,可以在方法实现时添加virtual,来让子类可以重写。
    //接口不能被实例化。
    //接口继承时不用实现,类继承时会实现。
    /*
     * interface I接口名
     * {
     * }
    */

    //显式实现接口就是 接口名.方法名 然后去实现。用于不同接口的相同方法实现。
    #endregion

    #region 练习1
    //实现登记方法。
    interface Register
    {
        void Register();
    }
    class Person : Register
    {
        public void Register()//注意类中默认为成员的修饰符为private,而接口中为public范围更大,要加public
        {
            Console.WriteLine("人登记");
        }
    }
    class Car : Register
    {
        public void Register()
        {
            Console.WriteLine("车登记");
        }
    }
    class House : Register
    {
        public void Register()
        {
            Console.WriteLine("房登记");
        }
    }
    #endregion

    #region 练习2:飞行与鸟
    abstract class SomeAnimal//动物抽象
    {
        protected abstract void Walk();
    }
    abstract class Bird : SomeAnimal//定义鸟抽象类
    {

    }
    interface Fly//定义飞行接口
    {
        void Fly();
    }
    interface Swim//游泳接口
    {
        void Swim();
    }
    class Sparrow : Bird, Fly//麻雀类
    {
        protected override void Walk()
        {
            Console.WriteLine("麻雀跳越");
        }
        public void Fly()
        {
            Console.WriteLine("麻雀飞翔");
        }
    }
    #endregion

    #region 练习3:传输数据
    interface USB
    {
        void Transmission();
    }
    abstract class StorageDevice : USB
    {
        abstract public void Transmission();
    }
    class U : StorageDevice
    {
        public override void Transmission()
        {
            Console.WriteLine("U盘传输");
        }
    }
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            Register r1 = new Car();
            r1.Register();

            USB uSB = new U();
            uSB.Transmission();
        }
    }
}

19、密封方法

#region 基础
//密封方法可以让虚方法和抽象方法不能再被子类的子类重写。
//通常在override前面使用。
#endregion

20、命名空间

using MySapce;
using ExSpace;
using UI;
namespace 命名空间
{
    #region 基本概念
    //1、命名空间是用来组织和重用代码。类似于工具包,类就是其中的工具。
    /*基本语法:
     * namaspace 空间名
     * {
     * }
     */
    //命名空间可以分成几次写。

    //使用不同的命名空间中的东西需要先引用using 命名空间名 或 指明出处 命名空间名.成员名
    //使用不同命名空间的同名成员时会报错。这时候需要指明出处。
    //命名空间可以包裹命名空间。
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            //Myclass1 c = new Myclass1();//会报错
            UI.Image i1 = new Image();
            Graph.Image i2 = new Graph.Image();
        }
    }
}

#region 练习
//using作用是引用其他命名空间。
namespace UI
{
    class Image
    {
        public Image()
        {
            Console.WriteLine("UI");
        }
    }
}
namespace Graph
{
    class Image
    {
        public Image()
        {
            Console.WriteLine("Graph");
        }
    }
}
#endregion

namespace ExSpace
{
    struct Myclass1
    {

    }
}
namespace MySapce
{
    class Myclass1
    {

    }
}

21、万物之父的方法

using System.Xml.Linq;

namespace 万物之父中的方法
{
    #region 静态方法
    //Equals:判断两边是否相等。引用类型判断的是地址是否相等。
    //ReferenceEquals:判断引用类型是否相等,值类型判断始终为false。
    #endregion
    #region 成员方法
    //GetType:作用:获取对象运行时的类型Type,和反射相关。
    //MemberWiseClone:返回一个浅拷贝对象。新对象中的引用变量和老对象中一致,改变会影响老的引用类型。但类中的值类型不会变。
    #endregion
    #region 虚方法
    //可以重写自己的Equals和GetHashCode
    //ToString:重写转字符串。
    #endregion

    #region 练习
    //打印玩家类属性
    class Player
    {
        string name;
        int hp;
        int atk;
        int def;
        int dod;
        public Player() 
        {
        }
        public Player(string name, int hp, int atk, int def, int dod)
        {
            this.name = name;
            this.hp = hp;
            this.atk = atk;
            this.def = def;
            this.dod = dod;
        }
        public override string ToString()
        {
            return $"玩家{name}, 血量{hp}, 攻击力{atk}, 防御力{def}, 闪避率{dod}";
        }
    }

    //克隆体,不受影响
    class Monster
    {
        public int hp = 25;
        public int atk = 5;
        public int def = 15;
        public int skillId = 55;
        public Monster Clone()
        {
            return this.MemberwiseClone() as Monster;
        }
        public override string ToString()
        {
            return $"血量{hp}, 攻击力{atk}, 防御力{def}, 技能id{skillId}";
        }
    }
    #endregion

    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(object.Equals(1, 1));
            Console.WriteLine(Object.ReferenceEquals(1, 1));//默认直接也能使用这两个方法,因为类是object的派生类。
            Player p1 = new Player("战士", 50, 3, 5, 10);
            Console.WriteLine(p1.ToString());
            Monster A = new Monster();
            Monster B = A.Clone();
            Console.WriteLine(A.ToString());
            Console.WriteLine(B.ToString());

            B.hp = 2;

            Console.WriteLine(A.ToString());
            Console.WriteLine(B.ToString());
        }
    }
}

22、string

namespace String
{
    internal class Program
    {
        #region 基本
        //string本质是char数组
        //转换char数组
        char[] charArr = "123".ToCharArray();
        #endregion
        static void Main(string[] args)
        {
            string s = "123456123456";
            //获取位置
            Console.WriteLine(s[1]);
            //获取长度
            Console.WriteLine(s.Length);
            //拼接,Format通常是用于更复杂的处理
            Console.WriteLine(string.Format("123{0}", s));
            //正向查找元素位置,没有返回-1,可以查找字符串。
            Console.WriteLine(s.IndexOf("345"));
            //反向查找位置,寻找最后面的元素,并返回正向下标。无返回-1
            Console.WriteLine(s.LastIndexOf("456"));//》9
            //删除,会删除下标出的元素,及以后的所有元素。会返回一个新字符串。注意不会改变原来的。
            //也能使用Remove(指定开始位置, 个数)来进行删除。
            Console.WriteLine(s.Remove(3));
            //替换指定字符串
            Console.WriteLine(s.Replace("123", "汪汪汪"));//>>汪汪汪456汪汪汪456
            //大小写转换
            Console.WriteLine("abc".ToUpper());//转大写
            Console.WriteLine("A".ToLower());//转大写
            //截取,包含指定位置开始,及其后面。也能指定个数在第二个参数。注意越界。
            Console.WriteLine(s.Substring(6));
            //切割,以特定字符或串来分隔并返回字符串数组。
            Console.WriteLine("999123888".Split("999")[1] + "------");//>>123888------
            
            #region 练习
            //练习截取和替换
            string str = "123".Substring(1, 2);
            Console.WriteLine(str);
            Console.WriteLine("12345".Replace("45", "123"));
            //1|2|3|4|5|6|7的分割变化
            str = "1|2|3|4|5|6|7";
            string[] sArr = str.Split("|");
            str = "";
            for(int i = 1; i < sArr.Length; i++)
            {
                str += sArr[i] + "|";
            }
            str += "8";
            Console.WriteLine(str);
            //String和string、Int32和int、Int16和short、Int64和long他们的区别是什么 ?
            //属于相同的关系,后者是前者的别称。

            /*string str = null; str = "123";
            string str2 = str; str2 = "321".str2 += "123";
            请问,上面这段代码,分配了多少个新的堆空间=3个*/

            //编写一个函数,将输入的字符串反转。不要使用中间商,你必须原地修改输入数组。交换过程中不使用额外空间比如:
            str = "hello";
            Console.Write("开始输出:");
            for(int i = 0; i < 5; i++)
            {
                sArr[i] = str[4 - i].ToString();
                Console.Write(sArr[i] + ",");
            }//开始输出:o,l,l,e,h,
            #endregion
        }
    }
}

23、stringBuilder

using System.Text;

namespace _StringBuilder
{
    internal class Program
    {
        static void Main(string[] args)
        {
            #region StringBuilder
            //用于经常修改字符串,它不会创建新字符串来提高性能。
            //需要引用命名空间。
            StringBuilder stringBuilder = new StringBuilder("123", 16);//存入123,并定义长度为16
            //StringBuilder有初始容量16。不够时会增加。
            //获取容量
            Console.WriteLine(stringBuilder.Capacity);//》16
            //获取字符串长度
            Console.WriteLine(stringBuilder.Length);
            #endregion

            #region 增删查改替换
            //增加
            stringBuilder.Append("123");//末尾增加
            Console.WriteLine(stringBuilder);//>>123123
            stringBuilder.AppendFormat("{0}", 1);
            Console.WriteLine(stringBuilder);//>>1231231
            //插入
            stringBuilder.Insert(1, "哈哈哈");//Insert(索引, string串);
            Console.WriteLine(stringBuilder);//1哈哈哈231231
            //删除
            stringBuilder.Remove(0, 1);
            Console.WriteLine(stringBuilder);//>>哈哈哈231231
            //清空
            stringBuilder.Clear();
            Console.WriteLine(stringBuilder);//>>
            //查找:stringBuilder[1];
            //改
            stringBuilder.Append("Hello");
            stringBuilder[0] = 'h';
            Console.WriteLine(stringBuilder);//>>hello
            //替换
            stringBuilder.Replace('h', 'H');
            Console.Write(stringBuilder);//》Hello
            if(stringBuilder.Equals("Hello"))
            {
                Console.WriteLine("相等");
            }
            #endregion
            //string和StringBuilder区别
            /*
             * string更容易产生垃圾,每次修改都会。
             * string方法更多。
             */
            //怎么优化内存
            //1、如何节约内存:少new对象。少产生垃圾
            //2、合理使用static
            //3、使用string和StringBuilder
        }
    }
}

24、类和结构体的区别

namespace 结构体和类区别
{
    #region 概述
    //结构体是值类型,存储在栈上。而类是引用类型,存储在堆上。
    //结构体没有继承和多态的,只有封装,所以不能使用保护修饰符。
    #endregion

    #region 细节
    //结构体声明时不能赋值
    //结构体不能声明无参构造函数
    //结构体声明有参构造函数无参构造不会被顶掉
    //结构体不能声明析构函数,类可以
    //结构体不能被继承
    //结构体需要再构造函数中初始化
    //结构体不能不能用static修饰,类可以
    //结构体内不能声明自己的类型,类可以
    struct Person
    {
        int age;
    }
    #endregion

    #region 特别之处
    //结构体能继承接口,因为接口是行为的抽象。
    #endregion

    #region 使用
    //需要继承用类,如玩家,怪物。数据的集合用结构体,如坐标。
    //
    #endregion
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

25、接口和抽象类区别

namespace 接口和抽象类
{
    internal class Program
    {
        static void Main(string[] args)
        {
            #region 相同点
            /* 1、都可以被继承
             * 2、都不能直接实例化
             * 3、子类必须实现方法
             * 4、都遵守里式替换原则
             */
            #endregion
        }
    }    
    #region 不同点
    //抽象类中可以有构造方法,接口没有
    abstract class Animal
    {
        int id;
        Animal(int id)
        {
            this.id = id;
        }
        void fun()
        {
            Console.Write(1);
        }
    }
    interface Fly
    {
        abstract void funt();
        void func();
    }
    //抽象类只能被单一继承,接口被类继承多个。
    //抽象类中可以有成员变量、成员方法、虚方法、抽象方法、静态方法并且他们可以被实现;接口中只能有没有被实现的方法。(8.0以后接口中也能有实现了的方法)
    //抽象类可以使用访问修饰符,接口建议不写,默认为public。
    //实现抽象方法需要加override,实现接口中的方法不用加override。
    #endregion

    #region 使用
    //表示对象的抽象用抽象类,行为用接口。
    #endregion
}

26、面相对象七大原则

七大原则实现目标:高内聚、低耦合。
使程序模块的可重用性、移植性加强。
1、单一职责原则:类应该专注于单一功能。
2、开闭原则:对扩展开放,对修改关闭。
3、里式替换原则:用父类装载子类。
4、依赖倒转原则:不依赖于具体事物,而依赖于其抽象。
5、迪米特原则:一个对象应该少于其他类发生关联。
6、接口分离原则:一个接口应该只提供一个功能,让其他想实现什么就继承什么接口,而非多个功能在一个接口中。
7、合成复用原则:尽量使用对象组合,而不是继承来达到复用目的。继承时强耦合,组合是弱耦合。

27、贪吃蛇

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson2;

namespace 贪吃蛇.Lesson1
{
    enum E_SceneType
    {
        Begin,
        game,
        end,
    }
    class Game
    {
        public const int w = 80;
        public const int h = 20;
        public static ISceneUpdate sceneUpdate;
        public Game()
        {   
            Console.CursorVisible = false;
            Console.SetWindowSize(w, h);
            Console.SetBufferSize(w, h);
            ChangeScene(E_SceneType.Begin);
        }
        public void Start()
        {
            while (true)
            {   
                if(sceneUpdate != null)
                {
                    sceneUpdate.Update();
                } 
            }
        }
        public static void ChangeScene(E_SceneType type)
        {
            Console.Clear();
            switch (type)
            {
                case E_SceneType.Begin:
                    sceneUpdate = new BeginScene();
                    break;
                case E_SceneType.game:
                    sceneUpdate = new GameScene();
                    break;
                case E_SceneType.end:
                    sceneUpdate = new EndScene();
                    break;
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 贪吃蛇.Lesson1
{
    interface ISceneUpdate
    {
        public void Update();
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson1;

namespace 贪吃蛇.Lesson2
{
    abstract class BeginAndEndScene : ISceneUpdate
    {
        protected int nowIndex = 0;
        protected string title;
        protected string option1;

        public abstract void EnterJDo();
        public void Update()
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.SetCursorPosition(Game.w / 2 - title.Length, 3);
            Console.Write(title);
            Console.ForegroundColor = nowIndex == 0 ? ConsoleColor.Red : ConsoleColor.Yellow;
            Console.SetCursorPosition(Game.w / 2 - option1.Length, 8);
            Console.Write(option1);
            Console.ForegroundColor = nowIndex == 1 ? ConsoleColor.Red : ConsoleColor.Yellow;
            Console.SetCursorPosition(Game.w / 2 - 4, 10);
            Console.Write("结束游戏");
            switch (Console.ReadKey(true).Key)
            {
                case ConsoleKey.W:
                    --nowIndex;
                    if(nowIndex < 0)
                    {
                        nowIndex = 0;
                    }
                    break;
                case ConsoleKey.S:
                    ++nowIndex;
                    if(nowIndex > 1)
                    {
                        nowIndex = 1;
                    }
                    break;
                case ConsoleKey.J:
                    EnterJDo();
                    break;
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson1;

namespace 贪吃蛇.Lesson2
{
    class BeginScene : BeginAndEndScene
    {
        public BeginScene()
        {
            title = "贪吃蛇";
            option1 = "进入游戏";
        }
        public override void EnterJDo()
        {
            if(nowIndex == 0)
            {
                Game.ChangeScene(E_SceneType.game);
            }
            else
            {
                Environment.Exit(0);
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson1;

namespace 贪吃蛇.Lesson2
{
    class EndScene : BeginAndEndScene
    {
        public EndScene()
        {
            title = "游戏结束";
            option1 = "回到开始";
        } 
        public override void EnterJDo()
        {
            if(nowIndex == 0)
            {
                Game.ChangeScene(E_SceneType.Begin);
            }
            else
            {
                Environment.Exit(0);
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson1;
using 贪吃蛇.Lesson4;
using 贪吃蛇.Lesson5;
using 贪吃蛇.Lesson6;

namespace 贪吃蛇.Lesson2
{
    class GameScene : ISceneUpdate
    {
        Map map;
        Snake snake;
        Food food;
        int count = 0;
        public GameScene()
        {
            map = new Map();
            snake = new Snake();
            food = new Food(snake);
        }
        public void Update()
        {
            if(count % 40000 == 0)
            {
                map.Draw();food.Draw();
                snake.Move();
                snake.Draw();
                if (snake.Die(map))
                {
                    Game.ChangeScene(E_SceneType.end);
                }
                snake.CheckEatFood(food);
                count = 0;
            }
            if (Console.KeyAvailable)
            {
                switch (Console.ReadKey(true).Key)
                {
                    case ConsoleKey.W:
                        snake.ChangeDir(E_MoveType.up);
                        break;
                    case ConsoleKey.S:
                        snake.ChangeDir(E_MoveType.down);
                        break;
                    case ConsoleKey.A:
                        snake.ChangeDir(E_MoveType.left);
                        break;
                    case ConsoleKey.D:
                        snake.ChangeDir(E_MoveType.right);
                        break;
                }
            }
            count++;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 贪吃蛇.Lesson3
{
    abstract class GameObject : IDraw
    {
        public Position position;
        public abstract void Draw();
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 贪吃蛇.Lesson3
{
    interface IDraw
    {
        public void Draw();
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 贪吃蛇.Lesson3
{
    struct Position
    {
        public int x;
        public int y;
        public Position(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
        public static bool operator ==(Position p1, Position p2)
        {
            if(p1.x == p2.x && p1.y == p2.y)
            {
                return true;
            }
            return false;
        }
        public static bool operator !=(Position p1, Position p2)
        {
            if(p1.x == p2.x && p1.y == p2.y)
            {
                return false;
            }
            return true;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson1;
using 贪吃蛇.Lesson3;
using 贪吃蛇.Lesson6;

namespace 贪吃蛇.Lesson4
{
    class Food : GameObject
    {
        public Food(Snake snake)
        {
            RandomPos(snake);
        }
        public override void Draw()
        {
            Console.SetCursorPosition(position.x, position.y);
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine("¤");
        }
        //随机事物并判断重合(蛇)
        public void RandomPos(Snake snake)
        {
            Random random = new Random();
            int x = random.Next(2, Game.w / 2 - 2) * 2;
            int y = random.Next(1, Game.h - 3);
            position = new Position(x, y);
            if (snake.CoincideFood(position))
            {
                RandomPos(snake);
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson3;

enum E_SnakeType
{
    Head,
    Body,
}

namespace 贪吃蛇.Lesson4
{
    class SnakeBody : GameObject
    {
        E_SnakeType snakeType;
        public SnakeBody(E_SnakeType snakeType, int x, int y)
        {
            this.snakeType = snakeType;
            position = new Position(x, y);
        }
        public override void Draw()
        {
            Console.SetCursorPosition(position.x, position.y);
            Console.ForegroundColor = snakeType == E_SnakeType.Head ? ConsoleColor.Yellow : ConsoleColor.Green;
            Console.WriteLine(snakeType == E_SnakeType.Head ? "●" : "◎");
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson3;

namespace 贪吃蛇.Lesson4
{
    class Wall : GameObject
    {
        public Wall(int x, int y)
        {
            position = new Position(x, y);
        }

        public override void Draw()
        {
            Console.SetCursorPosition(position.x, position.y);
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("▓");
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson1;
using 贪吃蛇.Lesson3;
using 贪吃蛇.Lesson4;

namespace 贪吃蛇.Lesson5
{
    class Map : IDraw
    {
        public Wall[] wall;
        int index;
        public Map()
        {
            index = 0;
            wall = new Wall[Game.w + (Game.h - 3) * 2];
            for(int i = 0; i < Game.w; i+=2)
            {
                wall[index] = new Wall(i, 0);
                index++;
                wall[index] = new Wall(i, Game.h - 2);
                index++;
            }
            for(int i = 1; i < Game.h - 2; i++)
            {
                wall[index] = new Wall(Game.w - 2, i);
                index++;
                wall[index] = new Wall(0, i);
                index++;
            }
        }

        public void Draw()
        {
            for(int i = 0; i < wall.Length; i++)
            {
                wall[i].Draw();
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 贪吃蛇.Lesson3;
using 贪吃蛇.Lesson4;
using 贪吃蛇.Lesson5;
//移动枚举
enum E_MoveType
{
    up,
    down,
    left,
    right,
}
namespace 贪吃蛇.Lesson6
{
    class Snake : IDraw
    {
        public SnakeBody[] bodys;
        public int bodyNum;
        E_MoveType moveType;
        public Snake()
        {
            bodys = new SnakeBody[200];
            bodys[0] = new SnakeBody(E_SnakeType.Head, 40, 10);
            bodyNum = 1;
            moveType = E_MoveType.right;
        }
        public void Draw()
        {
            for(int i = 0; i < bodyNum; i++)
            {
                bodys[i].Draw();
            }
        }
        public void Move()
        {
            SnakeBody lastBody = bodys[bodyNum - 1];
            Console.SetCursorPosition(lastBody.position.x, lastBody.position.y);
            Console.WriteLine("  ");
            if (bodyNum > 1)
            {
                for (int i = bodyNum - 1; i > 0; i--)
                {
                    bodys[i].position = bodys[i - 1].position;
                }
            }
            switch (moveType)
            {
                case E_MoveType.up:
                    bodys[0].position.y--;
                    break;
                case E_MoveType.down:
                    bodys[0].position.y++;
                    break; 
                case E_MoveType.left:
                    bodys[0].position.x -= 2;
                    break;
                case E_MoveType.right:
                    bodys[0].position.x += 2;
                    break;
            }
        }
        //转换方向
        public void ChangeDir(E_MoveType e_Move)
        {
            if(this.moveType == e_Move || 
                bodyNum > 1 && (
                this.moveType == E_MoveType.up && e_Move == E_MoveType.down ||
                this.moveType == E_MoveType.down && e_Move == E_MoveType.up ||
                this.moveType == E_MoveType.left && e_Move == E_MoveType.right ||
                this.moveType == E_MoveType.right && e_Move == E_MoveType.left))
            {
                return;
            }
            this.moveType = e_Move;
        }
        //撞墙、身体逻辑。
        public bool Die(Map map)
        {
            for(int i = 0; i < map.wall.Length; i++)
            {
                if (bodys[0].position == map.wall[i].position)
                {
                    return true;
                }
            }
            for (int i = 1; i < bodyNum; i++)
            {
                if (bodys[0].position == bodys[i].position)
                {
                    return true;
                }
            }
            return false;
        }
        //是否与食物重合
        public bool CoincideFood(Position position)
        {
            for(int i = 0; i < bodyNum; i++)
            {
                if(position == bodys[i].position)
                {
                    return true;
                }
            }
            return false;
        }
        public void CheckEatFood(Food food)
        {
            if (bodys[0].position == food.position)
            {
                food.RandomPos(this);
                AddBody();
            }
        }
        //增加身体
        void AddBody()
        {
            bodys[bodyNum] = new SnakeBody(E_SnakeType.Body, bodys[bodyNum - 1].position.x, bodys[bodyNum - 1].position.y);
            bodyNum++;
        }
    }
using 贪吃蛇.Lesson1;
using 贪吃蛇.Lesson6;

namespace 贪吃蛇
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Game game = new Game();
            game.Start();
        }
    }
}