2022-04-17 C# 第3篇——核心

一、GC 回收机制

​ 垃圾回收,英文简写 GC(Garbage Collector)
​ 垃圾回收的过程是在遍历堆(Heap)上动态分配的所有对象,通过识别它们是否被引用来确定哪些对象是垃圾,哪些对象仍要被使用
​ 所谓的垃圾就是没有被任何变量,对象引用的内容
​ 垃圾就需要被回收释放

​ 垃圾回收有很多种算法,比如:
​ 引用计数(Reference Counting)
​ 标记清除(Mark Sweep)
​ 标记整理(Mark Compact)
​ 复制集合(Copy Collection)

​ 注意:
​ GC只负责堆(Heap)内存的垃圾回收
​ 引用类型都是存在堆(Heap)中的,所以它的分配和释放都通过垃圾回收机制来管理

​ 栈(Stack)上的内存是由系统自动管理的
​ 值类型在栈(Stack)中分配内存的,他们有自己的生命周期,不用对他们进行管理,会自动分配和释放

​ C# 中内存回收机制的大概原理
​ 0代内存 1代内存 2代内存
​ 代的概念:
​ 代是垃圾回收机制使用的一种算法(分代算法)
​ 新分配的对象都会被配置在第0代内存中
​ 每次分配都可能会进行垃圾回收以释放内存(0代内存满时)

​ 在一次内存回收过程开始时,垃圾回收器会认为堆中全是垃圾,会进行以下两步
​ 1.标记对象 从根(静态字段、方法参数)开始检查引用对象,标记后为可达对象,未标记为不可达对象,不可达对象就认为是垃圾
​ 2.搬迁对象压缩堆 (挂起执行托管代码线程) 释放未标记的对象 搬迁可达对象 修改引用地址

​ 大对象总被认为是第二代内存 目的是减少性能损耗,提高性能
​ 不会对大对象进行搬迁压缩 85000字节(83kb)以上的对象为大对象

二、成员属性

​ 基本概念

  1. 用于保护成员变量

  2. 为成员属性的获取和赋值添加逻辑处理

  3. 解决 3P (Private、Public、Protected)的局限性

    public——内外访问

    private——内部访问

    protected——内部和子类访问

​ 属性可以让成员变量在外部只能获取、不能修改,或者,只能修改、不能获取

// 访问修饰符 属性类型 属性名            
// {                         
//     get{}                 
//     set{}                 
// }         

internal class Person
{
    private int age;
    private int money;

    // 属性的命名一般使用 帕斯卡命名法
    public string Name { get; set; }

    public int Money {
        get =>
            //解密处理
            money - 5;
        set =>
            //加密处理
            money = value + 5;
    }

    // get和set可以只有一个

    // 注意:
    // 只有一个时  没必要在前面加访问修饰符
    // 一般情况下 只会出现 只有 get的情况 基本不会出现只有set
    public bool Sex { get; }
    // set
    // {
    //     sex = value;
    // }

    // 作用:外部能得不能改的特征
    // 如果类中有一个特征是只希望外部能得不能改的 又没什么特殊处理
    // 那么可以直接使用自动属性
    public float Height {
        // 没有再get和set中写逻辑的需求或者想法
        get;
        private set;
    }

    // 成员属性中,get和set前可以加访问修饰符

    // 注意
    // 1.默认不加 会使用属性申明时的访问权限
    // 2.加的访问修饰符要低于属性的访问权限
    // 3.不能让get和set的访问权限都低于属性的权限
}                              

​ 1、成员属性概念:一般是用来保护成员变量的
​ 2、成员属性的使用和变量一样 外部用对象点出
​ 3、get 中需要 return 内容 ; set 中用 value 表示传入的内容
​ 4、get 和 set 语句块中可以加逻辑处理
​ 5、get 和 set 可以加访问修饰符,但是要按照一定的规则进行添加
​ 6、get 和 set 可以只有一个
​ 7、自动属性是属性语句块中只有 get 和 set,一般用于 外部能得不能改这种情况

三、索引器

​ 索引器:让对象可以像数组一样通过索引访问其中元素,使程序看起来更直观,更容易编写

// 访问修饰符 返回值 this[参数类型 参数名, 参数类型 参数名.....]
// {
//       内部的写法和规则和属性相同
//       get{}
//       set{}
// }

internal class Person
{
    private int age;

    private int[,]   array;
    private Person[] friends;
    private string   name;

    public Person this[int index] {
        get {
            // 可以写逻辑的 根据需求来处理这里面的内容
            // 索引器中可以写逻辑
            if (friends == null ||
                friends.Length - 1 < index)
                return null;

            return friends[index];
        }
        set {
            // value代表传入的值
            // 可以写逻辑的 根据需求来处理这里面的内容
            if (friends == null)
                friends = new[] { value };
            else if (index > friends.Length - 1)
                // 自己定了一个规则 如果索引越界 就默认把最后一个朋友顶掉
                friends[friends.Length - 1] = value;
            friends[index] = value;
        }
    }

    // 索引器可以重载

    // 重载的概念是——函数名相同 参数类型、数量、顺序不同
    public int this[int i, int j] {
        get => array[i, j];
        set => array[i, j] = value;
    }

    public string this[string str] {
        get {
            switch (str)
            {
                case "name":
                    return name;
                case "age":
                    return age.ToString();
            }

            return "";
        }
    }
}

​ 注意:结构体里面也支持索引器

四、静态成员

​ 概念:用 static 修饰的成员变量、成员方法、成员属性等 就称为静态成员
​ 特点:直接用类名点出来使用(全局性)
​ 生命周期:和程序同生共死
​ 程序运行后就会一直存在内存中,知道程序结束后才会释放,因此静态成员具有唯一性
​ 注意:
​ 1、静态函数中不能直接使用非静态成员
​ 2、非静态函数中可以直接使用静态成员

​ 常量和静态变量
​ 常量是特殊的静态变量
​ 相同点:
​ 他们都可以通过类名点出来使用
​ 不同点:
​ 1、const 必须初始化不能被修改 static 没有这个规则
​ 2、const 只能修饰变量,static 可以修饰很多
​ 3、const 不能写在访问修饰符前面,一定是写在变量申明前面 static 没有这个规则

五、静态类和静态构造函数

(一)静态类

​ 概念:用 static 修饰的类

​ 特点:

​ 1、只能包含静态成员

​ 2、不能被实例化

​ 作用:

​ 1、将常用的静态成员写在静态类中,方便使用

​ 2、静态类不能被实例化,更能体现工具类的唯一性
​ 比如 Console 就是一个静态类

(二)静态构造函数

​ 概念:在构造函数加上 static 修饰

​ 特点:
​ 1、静态类和普通类都可以有

​ 2、不能使用访问修饰符

​ 3、不能有参数

​ 4、只会自动调用一次

​ 作用:在静态构造函数中初始化静态变量

// 1.静态类中的静态构造函数                           
internal static class StaticClass         
{                                         
    public static int testInt  = 100;     
    public static int testInt2 = 100;     
                                          
    static StaticClass()                  
    {                                     
        Console.WriteLine("静态构造函数");      
        testInt  = 200;                   
        testInt2 = 300;                   
    }                                     
}                                         
                                          
// 2.普通类中的静态构造函数                           
internal class Test                       
{                                         
    public static int testInt = 200;      
                                          
    static Test()                         
    {                                     
        Console.WriteLine("静态构造");        
    }                                     
                                          
    public Test()                         
    {                                     
        Console.WriteLine("普通构造");        
    }                                     
}                                         
六、拓展方法

​ 概念:为现有非静态变量类型添加新方法
​ 作用:
​ 1、提升程序拓展性
​ 2、不需要再对象中重新写方法
​ 3、不需要继承来添加方法
​ 4、为别人封装的类型写额外的方法
​ 特点:
​ 1、一定是写在静态类中
​ 2、一定是个静态函数
​ 3、第一个参数为拓展目标
​ 4、第一个参数用 this 修饰

​ 语法:

访问修饰符 static 返回值 函数名(this 拓展类名 参数名, 参数类型 参数名,参数类型 参数名....)

七、运算符重载

​ 概念:让自定义类和结构体能够使用运算符

​ 使用关键字 operator

​ 特点:
​ 1、一定是一个公共的静态方法
​ 2、返回值写在 operator 前
​ 3、逻辑处理自定义

​ 作用:让自定义类和结构体对象可以进行运算
​ 注意:
​ 1、条件运算符需要成对实现
​ 2、一个符号可以多个重载
​ 3、不能使用 ref 和 out

​ 基本语法:

public static 返回类型 operator 运算符(参数列表)

internal class Point                                             
{                                                                
    public int x;                                                
    public int y;                                                
                                                                 
    public static Point operator +(Point p1, Point p2)           
    {                                                            
        var p = new Point();                                     
        p.x = p1.x + p2.x;                                       
        p.y = p1.y + p2.y;                                       
        return p;                                                
    }                                                            
                                                                 
    public static Point operator +(Point p1, int value)          
    {                                                            
        var p = new Point();                                     
        p.x = p1.x + value;                                      
        p.y = p1.y + value;                                      
        return p;                                                
    }                                                            
                                                                 
    public static Point operator +(int value, Point p1)          
    {                                                            
        var p = new Point();                                     
        p.x = p1.x + value;                                      
        p.y = p1.y + value;                                      
        return p;                                                
    }                                                                       
                                                                 
    // 注意 符号需要两个参数还是一个参数                                          
    public static Point operator -(Point p1, Point P2)           
    {                                                            
        return null;                                             
    }                                                            
                                                                 
    public static Point operator *(Point p1, Point P2)           
    {                                                            
        return null;                                             
    }                                                            
                                                                 
    public static Point operator /(Point p1, Point P2)           
    {                                                            
        return null;                                             
    }                                                            
                                                                 
    public static Point operator %(Point p1, Point P2)           
    {                                                            
        return null;                                             
    }                                                            
                                                                 
    public static Point operator ++(Point p1)                    
    {                                                            
        return null;                                             
    }                                                            
                                                                 
    public static Point operator --(Point p1)                    
    {                                                            
        return null;                                             
    }                                                            
                                                                   
    // 注意 符号需要两个参数还是一个参数                                          
    public static bool operator !(Point p1)                      
    {                                                            
        return false;                                            
    }                                                                      
                                                                 
    // 注意 符号需要两个参数还是一个参数                                          
    public static Point operator |(Point p1, Point p2)           
    {                                                            
        return null;                                             
    }                                                            
                                                                 
    public static Point operator &(Point p1, Point p2)           
    {                                                            
        return null;                                             
    }                                                            
                                                                 
    public static Point operator ^(Point p1, Point p2)           
    {                                                            
        return null;                                             
    }                                                            
                                                                 
    public static Point operator ~(Point p1)                     
    {                                                            
        return null;                                             
    }                                                            
                                                                 
    public static Point operator <<(Point p1, int num)           
    {                                                            
        return null;                                             
    }                                                            
                                                                 
    public static Point operator >> (Point p1, int num)          
    {                                                            
        return null;                                             
    }                                                                  
                                                                 
    // 1.返回值一般是 bool 值 也可以是其它的                                      
    // 2.相关符号必须配对实现                                               
    public static bool operator >(Point p1, Point p2)            
    {                                                            
        return false;                                            
    }                                                            
                                                                 
    public static bool operator <(Point p1, Point p2)            
    {                                                            
        return false;                                            
    }                                                            
                                                                 
    public static bool operator >=(Point p1, Point p2)           
    {                                                            
        return false;                                            
    }                                                            
                                                                 
    public static bool operator <=(Point p1, Point p2)           
    {                                                            
        return false;                                            
    }                                                            
                                                                 
    public static bool operator ==(Point p1, Point p2)           
    {                                                            
        return false;                                            
    }                                                            
                                                                 
    public static bool operator !=(Point p1, Point p2)           
    {                                                            
        return false;                                            
    }                                                            
                                                                 
    public static bool operator true(Point p1)                   
    {                                                            
        return false;                                            
    }                                                            
                                                                 
    public static bool operator false(Point p1)                  
    {                                                            
        return false;                                            
    }                                                   
}                                                                

​ 不可重载的运算符:

​ 逻辑与(&&) 逻辑或(||)

​ 索引符 []

​ 强转运算符 ()

​ 点.

​ 三目运算符? :

​ 赋值符号=

七、内部类和分部类

(一)内部类

​ 概念:在一个类中再申明一个类

​ 特点:使用时要用包裹者点出自己

​ 作用:亲密关系的变现

​ 注意:访问修饰符作用很大

internal class Person            
{                                
    public int    age;           
    public Body   body;          
    public string name;          
                                 
    public class Body            
    {                            
        private Arm leftArm;     
        private Arm rightArm;    
                                 
        private class Arm { }    
    }                            
}                                

(二)分部类

​ 概念:把一个类分成几部分申明

​ 关键字:partial

​ 作用:分部描述一个类,增加程序的拓展性

​ 注意:
​ 1、分部类可以写在多个脚本文件中
​ 2、分部类的访问修饰符要一致
​ 3、分部类中不能有重复成员

internal partial class Student        
{                                     
    public string name;               
    public bool   sex;                
                                      
    partial void Speak();             
}                                     
                                      
internal partial class Student        
{                                     
    public int number;                
                                      
    partial void Speak()              
    {                                 
        // 实现逻辑                        
    }                                 
                                      
    public void Speak(string str) { } 
}                                       

(三)分部方法

​ 概念:将方法的申明和实现分离
​ 特点:
​ 1、不能加访问修饰符 默认私有
​ 2、只能在分部类中申明
​ 3、返回值只能是 void
​ 4、可以有参数但不用 out 关键字

​ 局限性大,了解即可

八、继承

继承基本语法:class 类名: 父类名
1、单根性:只能继承一个父类
2、传递性:子类可以继承父类的父类。。。的所有内容
3、访问修饰符 对于成员的影响

​ 4、极奇不建议使用 在子类中申明和父类同名的成员

九、里氏替换原则

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

(一)is 和 as

​ 基本概念
​ is:判断一个对象是否是指定类对象
​ 返回值:bool 是为真,不是为假

​ as:将一个对象转换为指定类对象
​ 返回值:指定类型对象
​ 成功返回指定类型对象,失败返回null

​ 基本语法
​ 类对象 is 类名,该语句块会有一个 bool 返回值,true 和 false
​ 类对象 as 类名,该语句块会有一个对象返回值,对象和 null

internal class GameObject { }

internal class Player : GameObject
{
    public void PlayerAtk()
    {
        Console.WriteLine("玩家攻击");
    }
}

internal class Monster : GameObject
{
    public void MonsterAtk()
    {
        Console.WriteLine("怪物攻击");
    }
}

internal class Boss : GameObject
{
    public void BossAtk()
    {
        Console.WriteLine("Boss攻击");
    }
}


internal class Program
{
    private static void Main(string[] args)
    {
        Console.WriteLine("里氏替换原则");

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

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

        if (player is Player)
            // Player p = player as Player;
            // p.PlayerAtk();
            (player as Player).PlayerAtk();

        for (var 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();
    }
}
十、继承中的构造函数
internal class Test
{
    public int    testI;
    public string testStr;

    public Test() { }

    public Test(int i)
    {
        testI = i;
    }

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

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

// 特点
// 当申明一个子类对象时
// 先执行父类的构造函数
// 再执行子类的构造函数

// 注意:
// 1.父类的无参构造 很重要
// 2.子类可以通过base关键字 代表父类 调用父类构造

// 继承中构造函数的执行顺序
// 父类的父类的构造——>。。。父类构造——>。。。——>子类构造
internal class GameObject
{
    public GameObject()
    {
        Console.WriteLine("GameObject的构造函数");
    }
}

internal class Player : GameObject
{
    public Player()
    {
        Console.WriteLine("Player的构造函数");
    }
}

internal class MainPlayer : Player
{
    public MainPlayer()
    {
        Console.WriteLine("MainPlayer的构造函数");
    }
}

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

    // }

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

internal class Son : Father
{
    // 通过base调用指定父类构造

    public Son(int i) : base(i)
    {
        Console.WriteLine("Son的一个参数的构造");
    }

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

internal class Program
{
    private static void Main(string[] args)
    {
        Console.WriteLine("继承中的构造函数");

        var mp = new MainPlayer();

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

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

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

十一、万物之父和装箱拆箱

(一)万物之父

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

(二)装箱拆箱

​ 发生条件:用 object 存值类型(装箱),再把 object 转为值类型(拆箱)

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

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

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

// 装箱                  
object v = 3;   

// 拆箱                  
var intValue = (int) v;
十二、密封类

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

​ 意义:加强面向对象程序设计的规范性、结构性、安全性

十三、多态 vob

(一)多态的概念

​ 多态按字面的意思就是“多种状态”
​ 让继承同一父类的子类们 在执行相同方法时有不同的表现(状态)
​ 主要目的:同一父类的对象 执行相同行为(方法)有不同的表现
​ 解决的问题:让同一个对象有唯一行为的特征

(二)vob 的使用

// 运行时多态( vob、抽象函数、接口 )                                               
// v: virtual(虚函数)                                            
// o: override(重写)                                            
// b: base(父类)                                                
                                                             
internal class GameObject                                    
{                                                            
    public string name;                                      
                                                             
    public GameObject(string name)                           
    {                                                        
        this.name = name;                                    
    }                                                        
                                                             
    // 虚函数 可以被子类重写                                            
    public virtual void Atk()                                
    {                                                        
        Console.WriteLine("游戏对象进行攻击");                       
    }                                                        
}                                                            
                                                             
internal class Player : GameObject                           
{                                                            
    public Player(string name) : base(name) { }              
                                                             
    // 重写虚函数                                                  
    public override void Atk()                               
    {                                                        
        // base 的作用                                            
        // 代表父类 可以通过base来保留父类的行为                              
        base.Atk();                                          
        Console.WriteLine("玩家对象进行攻击");                       
    }                                                        
}                                                            
                                                             
internal class Monster : GameObject                          
{                                                            
    public Monster(string name) : base(name) { }             
                                                             
    public override void Atk()                               
    {                                                        
        Console.WriteLine("怪物对象进行攻击");                       
    }                                                        
}                                                                
                                                             
internal class Program                                       
{                                                            
    private static void Main(string[] args)                  
    {                                                        
        Console.WriteLine("多态vob");                                        
                                                             
        Father f = new Son();                                
        f.SpeakName();                                       
        (f as Son).SpeakName();                                      
                                                             
        // 多态的使用                                             
        GameObject p = new Player("xxx");                    
        p.Atk();                                             
        (p as Player).Atk();                                 
                                                             
        GameObject m = new Monster("xxx");                   
        m.Atk();                                             
        (m as Monster).Atk();                                     
    }                                                        
}                                                            

​ 多态:让同一类型的对象,执行相同行为时有
​ 解决的问题: 让同一对象有唯一的行为特征
​ vob:
​ v:virtual 虚函数
​ o:override 重写
​ b:base 父类
v 和 o 一定是结合使用的,来实现多态
b 是否使用根据实际需求,保留父类行为

十四、抽象类和抽象方法

(一)抽象类

​ 概念:被抽象关键字abstract修饰的类
​ 特点:
​ 1、不能被实例化的类
​ 2、可以包含抽象方法
​ 3、继承抽象类必须重写其抽象方法

(二)抽象方法

​ 又叫纯虚方法,用 abstract 关键字修饰的方法
​ 特点:
​ 1、只能在抽象类中申明
​ 2、没有方法体
​ 3、不能是私有的
​ 4、继承后必须实现,用 override 重写

internal abstract class Fruits                    
{                                                 
    public string name;                           
                                                  
    // 抽象方法 是一定不能有函数体的                             
    public abstract void Bad();                   
                                                  
    public virtual void Test()                    
    {                                             
        // 可以选择是否写逻辑                               
    }                                             
}                                                 
                                                  
internal class Apple : Fruits                     
{                                                 
    public override void Bad() { }                
    // 虚方法是可以由我们子类选择性来实现的                          
    // 抽象方法必须要实现                                   
}                                                 
                                                  
internal class SuperApple : Apple                 
{                                                 
    // 虚方法和抽象方法 都可以被子类无限的 去重写                      
    public override void Bad()                    
    {                                             
        base.Bad();                               
    }                                             
                                                  
    public override void Test()                   
    {                                             
        base.Test();                              
    }                                             
}

internal class Program                      
{                                           
    private static void Main(string[] args) 
    {                                       
        Console.WriteLine("抽象类和抽象方法");      
        // 抽象不能被实例化                          
        // Thing t = new Thing();            
        // 但是 可以遵循里氏替换原则 用父类容器装子类            
        Thing t = new Water();              
    }                                       
}                                           
十五、接口

​ 接口是行为的抽象规范,它也是一种自定义类型
​ 关键字 :interface

​ 接口申明的规范
​ 1、不包含成员变量
​ 2、只包含方法、属性、索引器、事件
​ 3、成员不能被实现
​ 4、成员可以不用写访问修饰符,不能是私有的
​ 5、接口不能继承类,但是可以继承另一个接口

​ 接口的使用规范
​ 1、类可以继承多个接口
​ 2、类继承接口后,必须实现接口中所有成员

​ 特点:
​ 1、它和类的申明类似
​ 2、接口是用来继承的
​ 3、接口不能被实例化,但是可以作为容器存储对象

// 接口关键字:interface                                    
// 语法:                                                
// interface 接口名                                     
// {                                                 
// }                                                 
// 一句话记忆:接口是抽象行为的“基类”                                 
// 接口命名规范 帕斯卡前面加个 I                                    
                                                     
internal interface IFly                              
{                                                    
    string Name { get; set; }                        
                                                     
    int this[int index] { get; set; }                
                                                     
    void Fly();                                      
                                                     
    event Action doSomthing;                         
}                                                             
                                                     
// 接口用来继承                                             
internal class Animal { }                            
                                                     
// 1.类可以继承1个类,n个接口                                    
// 2.继承了接口后 必须实现其中的内容 并且必须是public的                    
internal class Person : Animal, IFly                 
{                                                    
    // 3.实现的接口函数,可以加virtual再在子类重写                           
    public virtual void Fly() { }                    
                                                     
    public string Name { get; set; }                 
                                                     
    public int this[int index] {                     
        get => 0;                                    
        set { }                                      
    }                                                
                                                     
    public event Action doSomthing;                  
}                                                    
                                                     
// 接口可以继承接口                                                                              
// 接口继承接口时  不需要实现                                     
// 待类继承接口后  类自己去实现所有内容                                
internal interface IWalk                             
{                                                    
    void Walk();                                     
}                                                    
                                                     
internal interface IMove : IFly, IWalk { }           
                                                     
internal class Test : IMove                          
{                                                    
    public int this[int index] {                     
        get => throw new NotImplementedException();  
        set => throw new NotImplementedException();  
    }                                                
                                                     
    public string Name {                             
        get => throw new NotImplementedException();  
        set => throw new NotImplementedException();  
    }                                                
                                                     
    public event Action doSomthing;                  
                                                     
    public void Fly()                                
    {                                                
        throw new NotImplementedException();         
    }                                                
                                                     
    public void Walk()                               
    {                                                
        throw new NotImplementedException();         
    }                                                
}                                                                                     
                                                     
// 当一个类继承两个接口                                         
// 但是接口中存在着同名方法时                                      
// 注意:显示实现接口时 不能写访问修饰符                                           
internal interface IAtk                              
{                                                    
    void Atk();                                      
}                                                    
                                                     
internal interface ISuperAtk                         
{                                                    
    void Atk();                                      
}                                                    
                                                     
internal class Player : IAtk, ISuperAtk              
{                                                    
    // 显示实现接口 就是用 接口名.行为名 去实现                         
    void IAtk.Atk() { }                              
                                                     
    void ISuperAtk.Atk() { }                         
                                                     
    public void Atk() { }                            
}                                                      
                                                     
internal class Program                               
{                                                    
    private static void Main(string[] args)          
    {                                                
        Console.WriteLine("接口");                     
        // 4.接口也遵循里氏替换原则                              
        IFly f = new Person();                       
                                                     
        IMove im   = new Test();                     
        IFly  ifly = new Test();                     
        IWalk iw   = new Test();                     
                                                     
        IAtk      ia  = new Player();                
        ISuperAtk isa = new Player();                
        ia.Atk();                                    
        isa.Atk();                                   
                                                     
        var p = new Player();                        
        (p as IAtk).Atk();                           
        (p as ISuperAtk).Atk();                      
        p.Atk();                                     
    }                                                
}                                                    
十六、密封方法

​ 用密封关键字 sealed 修饰的重写函数
​ 作用:让虚方法或者抽象方法之后不能再被重写
​ 特点:和 override 一起出现

十七、命名空间

​ 概念:命名空间是用来组织和重用代码的
​ 作用:就像是一个工具包,类就像是一件一件的工具,都是申明在命名空间中的

// 基本语法                                             
// namespace 命名空间名                                  
// {                                                
//     类                                              
//     类                                              
// }                                                
namespace MyGame                                   
{                                                  
    internal class GameObject { }                  
}                                                  
                                                   
namespace MyGame                                   
{                                                  
    internal class Player : GameObject { }         
}                                                  

// 不同命名空间中允许有同名类                                                                            
namespace MyGame2                                  
{                                                  
    // 在不同的命名空间中 是可以有同名类的                           
    internal class GameObject { }                  
}                                                  
                                                   
// 命名空间可以包裹命名空间                                                 
namespace MyGame                                   
{                                                  
    namespace UI                                   
    {                                              
        internal class Image { }                   
    }                                              
                                                   
    namespace Game                                 
    {                                              
        internal class Image { }                   
    }                                              
}                                                  
十八、万物之父中的方法

(一)静态方法

  1. public static bool Equals(object? objA, object? objB);
// 静态方法 Equals 判断两个对象是否相等
// 最终的判断权,交给左侧对象的Equals方法,
// 不管值类型引用类型都会按照左侧对象Equals方法的规则来进行比较
Console.WriteLine(Equals(1, 1));          
Test t = new Test();                    
Test t2 = new Test();                   
Console.WriteLine(Object.Equals(t, t2));
  1. public static bool ReferenceEquals(object? objA, object? objB);
// 静态方法 ReferenceEquals             
// 比较两个对象是否是相同的引用,主要是用来比较引用类型的对象。  
// 值类型对象返回值始终是false。               
Console.WriteLine(Object.ReferenceEquals(t, t2));

(二)成员方法

  1. public Type GetType();
// 普通方法GetType                                
// 该方法在反射相关知识点中是非常重要的方法,之后我们会具体的讲解这里返回的Type类型。
// 该方法的主要作用就是获取对象运行时的类型Type,                  
// 通过Type结合反射相关知识点可以做很多关于对象的操作。               
var t    = new Test();                       
var type = t.GetType();                      
  1. protected object MemberwiseClone ();
// 普通方法 MemberwiseClone               
// 该方法用于获取对象的浅拷贝对象,口语化的意思就是会返回一个新的对象,
// 但是新对象中的引用变量会和老对象中一致。              
var t2 = t.MemberwiseClone();                 

(三)虚方法

  1. public virtual bool Equals(object? obj);
// 虚方法Equals                                    
// 默认实现还是比较两者是否为同一个引用,即相当于ReferenceEquals。      
// 但是微软在所有值类型的基类System.ValueType中重写了该方法,用来比较值相等。
// 我们也可以重写该方法,定义自己的比较相等的规则                      
  1. public virtual int GetHashCode();
// 虚方法GetHashCode                                 
// 该方法是获取对象的哈希码                                   
// (一种通过算法算出的,表示对象的唯一编码,不同对象哈希码有可能一样,具体值根据哈希算法决定),
// 我们可以通过重写该函数来自己定义对象的哈希码算法,正常情况下,我们使用的极少,基本不用。   
  1. public virtual string? ToString();
// 虚方法ToString                                     
// 该方法用于返回当前对象代表的字符串,我们可以重写它定义我们自己的对象转字符串规则,       
// 该方法非常常用。当我们调用打印方法时,默认使用的就是对象的ToString方法后打印出来的内容。
Console.WriteLine(t);
十九、string
// 1.字符串指定位置获取

// 字符串本质是char数组                                                                      
var str = "xxx";                                                                    
Console.WriteLine(str[0]);                                                          
// 转为char数组                                                                          
var chars = str.ToCharArray();                                                      
Console.WriteLine(chars[1]);
for (var i = 0; i < str.Length; i++) Console.WriteLine(str[i]);                     
                                                                                    
// 2.字符串拼接                                                                  
                                                                                    
str = string.Format("{0}{1}", 1, 3333);                                             
Console.WriteLine(str);                                                             
                                                                                    
// 3.正向查找字符位置                                                               
                                                                                    
str = "我是xxx!";                                                                     
var index = str.IndexOf("x");                                                       
Console.WriteLine(index);                                                              
index = str.IndexOf("吊");                                                           
Console.WriteLine(index);                                                         
                                                                                    
// 4.反向查找指定字符串位置                                                            
                                                                                    
str = "我是xxxxxx";                                                                
index = str.LastIndexOf("xxx");                                                     
Console.WriteLine(index);                                                           
index = str.LastIndexOf("xxx");                                                     
Console.WriteLine(index);                                                           
                                                                                    
// 5.移除指定位置后的字符                                                             
                                                                                    
str = "我是xxxxxx";                                                                   
str.Remove(4);                                                                      
Console.WriteLine(str);                                                             
str = str.Remove(4);                                                                
Console.WriteLine(str);                                                                         
// 执行两个参数进行移除                                                                        
// 参数1 开始位置                                                                          
// 参数2 字符个数                                                                          
str = str.Remove(1, 1);                                                             
Console.WriteLine(str);                                                             
                                                                                    
// 6.替换指定字符串                                                                
                                                                                    
str = "我是xxxxxx";                                                                   
str.Replace("xxx", "aaa");                                                          
Console.WriteLine(str);                                                             
str = str.Replace("xxx", "aaa");                                                    
Console.WriteLine(str);                                                             
                                                                                    
// 7.大小写转换                                                                  
                                                                                    
str = "ksdfasdfasfasdfsasdfasdf";                                                   
str.ToUpper();                                                                      
Console.WriteLine(str);                                                             
str = str.ToUpper();                                                                
Console.WriteLine(str);                                                                        
str.ToLower();                                                                      
Console.WriteLine(str);                                                             
str = str.ToLower();                                                                
Console.WriteLine(str);                                                             
                                                                                    
// 8.字符串截取                                                                  
                                                                                    
str = "xxxxxx";                                                                     
// 截取从指定位置开始之后的字符串                                                                   
str.Substring(2);                                                                   
Console.WriteLine(str);                                                             
str = str.Substring(2);                                                             
Console.WriteLine(str);                                                                     
// 参数一 开始位置                                                                          
// 参数二 指定个数                                                                          
// 不会自动的帮助你判断是否越界 你需要自己去判断                                                           
str = str.Substring(2, 2);                                                          
Console.WriteLine(str);                                                             
                                                                                    
// 9.字符串切割                                                                  
                                                                                    
str = "1_1|2_2|3_3|5_1|6_1|7_2|8_3";                                                
var strs = str.Split('|');                                                          
for (var i = 0; i < strs.Length; i++) Console.WriteLine(strs[i]);                   
二十、StringBuilder

​ string 是特殊的引用,每次重新赋值或者拼接时会分配新的内存空间,如果一个字符串经常改变会非常浪费空间

​ C# 提供的一个用于处理字符串的公共类:StringBuilder
​ 主要解决的问题是:修改字符串而不创建新的对象,需要频繁修改和拼接的字符串。可以使用它,可以提升性能
​ 使用前需要引用命名空间

// 初始化 直接指明内容                                     

var str = new StringBuilder("123123123");              
Console.WriteLine(str);                                
                                                       
// 容量                                               
//StringBuilder存在一个容量的问题,每次往里面增加时 会自动扩容                
//获得容量                                                 
Console.WriteLine(str.Capacity);                       
//获得字符长度                                               
Console.WriteLine(str.Length);                         
                                                       
// 增删查改替换                                         
                                                       
// 增                                                    
str.Append("4444");                                    
Console.WriteLine(str);                                                  
str.AppendFormat("{0}{1}", 100, 999);                  
Console.WriteLine(str);                       
// 插入                                                   
str.Insert(0, "xxx");                                  
Console.WriteLine(str);                                
// 删                                                    
str.Remove(0, 10);                                     
Console.WriteLine(str);                                
// 清空                                                   
//str.Clear();                                         
//Console.WriteLine(str);                              
// 查                                                    
Console.WriteLine(str[1]);                             
// 改                                                    
str[0] = 'A';                                          
Console.WriteLine(str);                                
// 替换                                                   
str.Replace("1", "x");                                 
Console.WriteLine(str);                                
                                                       
// 重新赋值 StringBuilder                                   
str.Clear();                                           
str.Append("123123");                                  
Console.WriteLine(str); 

// 判断StringBuilder是否和某一个字符串相等                           
if (str.Equals("12312")) Console.WriteLine("相等");      
二十一、结构体和类的区别

(一)区别概述

​ 结构体和类最大的区别是在存储空间上的,因为结构体是值,类是引用,
​ 因此他们的存储位置一个在栈上,一个在堆上,
​ 通过之前知识点的学习,我相信你能够从此处看出他们在使用的区别——值和引用对象在赋值时的区别。

​ 结构体和类在使用上很类似,结构体甚至可以用面向对象的思想来形容一类对象。
​ 结构体具备着面向对象思想中封装的特性,但是它不具备继承和多态的特性,因此大大减少了它的使用频率。
​ 由于结构体不具备继承的特性,所以它不能够使用 protected 保护访问修饰符。

(二)细节区别

  1. 结构体是值类型,类是引用类型
  2. 结构体存在栈中,类存在堆中
  3. 结构体成员不能使用 protected 访问修饰符,而类可以
  4. 结构体成员变量申明不能指定初始值,而类可以
  5. 结构体不能申明无参的构造函数,而类可以
  6. 结构体申明有参构造函数后,无参构造不会被顶掉
  7. 结构体不能申明析构函数,而类可以
  8. 结构体不能被继承,而类可以
  9. 结构体需要在构造函数中初始化所有成员变量,而类随意
  10. 结构体不能被静态 static 修饰(不存在静态结构体),而类可以
  11. 结构体不能在自己内部申明和自已一样的结构体变量,而类可以
  12. 特别地,结构体可以继承接口,因为接口是行为的抽象

(三)如何选择

  1. 想要用继承和多态时,直接淘汰结构体,比如玩家、怪物等等
  2. 对象是数据集合时,优先考虑结构体,比如位置、坐标等等
  3. 从值类型和引用类型赋值时的区别上去考虑,比如经常被赋值传递的对象,并且
  4. 改变赋值对象,原对象不想跟着变化时,就用结构体。比如坐标、向量、旋转等等
二十二、抽象类和接口的区别

(一)相同点

  1. 都可以被继承
  2. 都不能直接实例化
  3. 都可以包含方法申明
  4. 子类必须实现未实现的方法
  5. 都遵循里氏替换原则

(二)区别

  1. 抽象类中可以有构造函数;接口中不能
  2. 抽象类只能被单一继承;接口可以被继承多个
  3. 抽象类中可以有成员变量;接口中不能
  4. 抽象类中可以申明成员方法,虚方法,抽象方法,静态方法;接口中只能申明没有实现的抽象方法
  5. 抽象类方法可以使用访问修饰符;接口中建议不写,默认 public

(三)如何选择

​ 表示对象的用抽象类,表示行为拓展的用接口
​ 不同对象拥有的共同行为,我们往往可以使用接口来实现
​ 举个例子:动物是一类对象,我们自然会选择抽象类;而飞翔是一个行为,我们自然会选择接口。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔗理苦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值