• 八、面向对象 之 封装


    点击访问官网 class

    1、访问修饰符

    • 点击访问官网 访问修饰符
    • 以下列举常用3个(注:不写默认private)
    修饰符子类外部
    public
    protected×
    private××

    2、默认值

    • 引用类型默认值都是 null,值类型略
    • 可以通过 default(类型) 查看任意类型的默认值

    3、类

    [访问修饰符] class 类名
    {
        // 特征 --- 成员变量
        // 行为 --- 成员方法
        // 保护特征 --- 成员属性
        
        // 构造函数 + 析构函数
        // 索引器
        // 运算符重载
        
        // 静态成员
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.1、特征 — 成员变量

    class Person
    {
        public string name;
        protected int age;
        private bool sex;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.2、行为 — 成员方法

    class Person
    {
        public void Speak(string word){
            Console.WriteLine(word);
        }
        
        private int OneYearPass(){
            return this.age++;
        } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.3、保护特征 — 成员属性

    3.3.1、概念

    • 保护成员变量,为成员变量的获取和赋值添加逻辑处理
    • 解决访问修饰符的局限性,成员属性可以让成员变量在外部 只能获取不能修改 or只能修改不能获取
      • setter/getter

    3.3.2、基本语法

    • 属性名采用 帕斯卡命名法;就是大写首字母
    访问修饰符 类型 属性名
    {
        get{}
        set{}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 常规 Demo
      • set 里有一个关键字 value 用于表示 入参
    class Person
    {
        // 成员变量
        private int money;
        
        // 成员属性
        public int Money
        {
            get
            {
                // 写一些逻辑保护成员变量...
                // money -= 5;
                return money;
            }
            set
            {
                // 写一些逻辑保护成员变量...
                // value += 5;
                // value 用于表示 入参
                money = value;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.3.3、get 和 set 前可加访问修饰符

    • 不加时默认使用属性声明时的访问修饰符
    • 手动加访问修饰符要低于属性访问权限
    • 不能 get 和 set 的访问修饰符都低于属性访问权限
    class Person
    {
        // 成员变量
        private int money;
        
        // 成员属性
        public int Money // 只能写不能读
        {
            private get // 不可读
            {
                return money;
            }
            set
            {
                money = value;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • get 和 set 可以只有一个
    class Person
    {
        // 成员变量
        private readonly int money;
        
        // 成员属性
        public int Money // 只读
        {
            get
            {
                return money;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 自动属性(没有特殊处理时可以使用,可以简化一点代码)
      • 属性没有对应的成员变量,C#会自动维护一个它的成员变量
    class Person
    {
        // 自动属性(类里并没有 money 属性)
        public int Money
        {
            get
            {
                return money;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • Lambda写法
    public int Money
    {
        get => money;
        set => money = value;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 如果没有额外逻辑的话可以简写
    public int Money{ set; get; }
    //或者直接省略
    public int Money;
    
    // Others
    public int Money{ private set; get; }
    public int Money{ set; private get; }
    public int Money{ get { return money - 5; } }
    public int Money{ set { money = value + 5; } }
    public int Money{ get; }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.4、构造函数

    • 普普通通的构造函数
    class Person
    {
        public Person(string name, int age, bool sex){
            this.name = name;
            this.age = age;
            this.sex = sex;
        } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 构造函数的特殊写法(复用):可以通过:this(参数列表) 重用构造函数
    class Person
    {
        public Person() { }
    
        public Person(string name):this() // 调了无参构造
        {
            this.name = name;
        }
    
        public Person(int age, bool sex)
        {
            this.age = age;
            this.sex = sex;
        }
    
        public Person(string name, int age, bool sex): this(age, sex) // 调了上面的构造
        {
            this.name = name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.5、析构函数

    • 当引用类型的堆内存被回收时调用析构
    • C# 有自动垃圾回收机制GC(学过java的小伙伴应该知道)
    • 所以基本不会用这个函数,除非你就是想在这个类被清理之前做点特殊处理
    • 游戏开发中更不会用到这个函数,了解即可
    • 语法:~类名(){}

    *垃圾回收机制:

    • 垃圾回收,英文简写 GC ( Garbage Co1 lector)
    • 垃圾回收的过程是在遍历堆 (Heap)上动态分配的所有对象,通过识别它们是否被引用来确定哪些对象是垃圾,哪些对象仍要被使用
    • 所谓的垃圾就是没有被任何变量、对象引用的内容;垃圾就需要被回收释放
    • 垃圾回收有很多种算法,比如:
      • 引用计数 ( Reference Counting)
      • 标记清除 ( Mark Sweep)
      • 标记整理 ( Mark Compact)
      • 复制集合 ( Copy Co1 lection)
    • 注意:
      • GC只负责堆 (Heap)内存的垃圾回收
      • 引用类型都是存在堆 (Heap)中的,所以它的分配和释放都通过垃圾回收机制来管理
      • 栈 ( Stack)上的内存是由系统自动管理的
      • 值类型在栈( Stack)中分配内存的,他们有自己的生命周期,不用对他们进行管理,会自动分配和释放
    • C#中内存回收机制的大概原理
      • 0代内存 1代内存 2代内存
      • 代的概念
        • 代是垃圾回收机制使用的一种算法 (分代算法)
        • 新分配的对象都会被配置在第 θ代内存 中
        • 每次分配都可能会进行垃圾回收以释放内存(0代内存满时)
      • 在一次内存回收过程开始时,垃圾回收器会认为堆中全是垃圾,会进行以下两步
        • 标记对象从根 ( 静态字段、方法参数 ) 开始检查引用对象,标记后为可达对象,未标记为不可达对象,不可达对象就认为是垃圾
        • 搬迁对象压缩堆 ( 挂起执行托管代码线程 ) ,释放未标记的对象,搬迁可达对象,修改引用地址
      • 大对象总被认为是第二代内存,目的是减少性能损耗,提高性能
      • 不会对大对象进行搬迁压缩,85080字节(83kb)以上的对象为大对象

    3.6、索引器

    3.6.1、基本概念

    • 让对象可以像数组一样通过索引访问其中元素

    3.6.2、语法

    访问修饰符 返回类型 this[参数类型 参数名, 参数类型 参数名 ...]
    {
        get{}
        set{}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.6.3、使用

    • 此set、get使用上和属性的set、get一样
    class Person
    {
        // 变量部分
        private Person[] friends;
    
        // 索引器
        public Person this[int index]
        {
            get 
            { 
                return friends[index]; 
            }
            set 
            { 
                friends[index] = value; 
            }
        }
    }
    
    // 某方法内↓↓↓
    Person p = new Person();
    p[0] = new Person();
    Console.WriteLine(p[0]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.6.4、索引器中可以写逻辑

    • 正常上面的代码肯定会报空指针异常,补上一些逻辑补丁,让上面的代码可以正运行
    class Person
    {
        // 变量部分
        private Person[] friends;
    
        // 索引器
        public Person this[int index]
        {
            get 
            { 
                return friends[index >= 10 ? 9 : index]; 
            }
            set 
            { 
                if (friends == null) {
                    friends = new Person[10];
                }
                friends[index >= 10 ? 9 : index] = value; 
            }
        }
    }
    
    // 某方法内↓↓↓
    Person p = new Person();
    p[0] = new Person();
    Console.WriteLine(p[0]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3.6.5、索引器可以重载

    • 上面的代码写个重载的demo
    class Person
    {
        // 变量部分
        private Person[] friends;
    
        // 索引器
        public Person this[int index, string key] // 随便定义,反正逻辑如何也是可以随便写
        {
            get 
            { 
                return friends[index]; 
            }
            set 
            {
                if (friends == null) {
                    friends = new Person[10];
                }
                friends[index] = value; 
            }
        }
    }
    
    // 某方法内↓↓↓
    Person p = new Person();
    p[0, ""] = new Person();
    Console.WriteLine(p[0, ""]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3.7、运算符重载

    3.7.1、概念

    • 让自定义类/结构体能使用运算符
    • 关键字 operator
    • 特点
      • 一定是一个公共的静态方法
      • 返回类型写在 operator 前面
    • 注意
      • 条件运算符需要成对实现
      • 一个符号可以多个重载
      • 不能使用 ref 和 out

    3.7.2、语法

    public static 返回类型 operator 运算符(参数列表)
    {
        // ...
    }
    
    • 1
    • 2
    • 3
    • 4

    3.7.3、栗子

    class Point
    {
        public int x;
        public int y;
    
        public Point(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    
        // + 重载 1
        public static Point operator +(Point p1, Point p2) {
            return new Point(p1.x + p2.x, p1.y + p2.y);
        }
    
        // + 重载 2
        public static Point operator +(Point p, int add) {
            return new Point(p.x + add, p.y + add);
        }
    
    
        // + 重载 3
        public static Point operator +(int add, Point p) {
            return p + add;
        }
    }
    
    // 某方法内↓↓↓
    Console.WriteLine(new Point(1, 2) + new Point(2, 3));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    3.7.4、可重载运算符有哪些

    • 算数运算符
    • 逻辑运算符中的 !
    • 位运算符
    • 条件运算符
      • 条件运算符重载必须成对实现,例如重载了 < 就必须重载 >

    3.8、静态成员

    • static 修饰的 成员变量、成员属性、方法等 就称为静态成员
    • 直接用类名点出来使用
      • 静态成员从程序开始运行时就在静态内存区域分配空间,在第一次使用时会创建好唯一的一份,直到程序结束才会释放这部分空间
    • 静态函数中不能使用非静态成员
    • 非静态函数可以使用静态成员

    *const 和 static 区别

    • 常量可以理解为特殊的静态量
    • 相同
      • 都可以类名直接点出来
    • 不同
    • const 必须初始化,不能修改,static 随意
    • const 只能修饰变量,static 随意
    • const 必须写在访问修饰符后面,static 随意
    class Yuan
    {
        // 静态成员变量
        public static float PI = 3.14159226f;
    
        // 普通成员变量
        public float r;
    
        // 静态成员方法
        public static float Area(float r) {
            return PI * r * r;
        }
    
        // 普通成员方法
        public float DemoFun()
        {
            return PI * r * r;
        }
    }
    
    // 某方法内↓↓↓
    Console.WriteLine(Yuan.PI); // 3.141592
    Console.WriteLine(Yuan.Area(10f)); // 314.1592
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.9、静态类

    • 用 static 修饰的 类
    • 特点
      • 只能包含静态成员
      • 不能被实例化
    • 作用
      • 将常用的静态成员写在静态类中,方便使用
      • 静态类不能被实例化,更能体现工具类的 唯一性
        • 比如 Console 就是一个静态类

    3.10、静态构造函数

    • 用 static 修饰的 构造函数
    • 特点
      • 静态类和普通类都可以有
      • 不能使用访问修饰符
      • 不能有参数
      • 只会自动调用一次
    • 作用
      • 在静态构造函数中初始化 静态变量

    3.11、拓展方法

    3.11.1、概念

    • 为现有 非静态 变量类型 添加 新方法
    • 作用
      • 提高程序拓展性
      • 不需要在对象中重写方法
      • 不需要继承来添加方法
      • 为别人封装的类型写额外方法
    • 特点
      • 一定是写在静态类中
      • 一定是静态函数
      • 第一个参数为拓展目标
      • 第一个参数用this修饰
    • 注意!!!
      • 如果拓展名和原有名重合,则拓展无效

    3.11.2、语法

    访问修饰符 static 返回类型 函数名(this 拓展类型 参数名, 参数类型 参数名, 参数类型 参数名 ...)
    {
        // ...
    }
    
    • 1
    • 2
    • 3
    • 4

    3.11.3、栗子

    // 拓展方法
    static class Tools
    {
        /// 
        /// 为 int 拓展一个成员方法(实例化后才能使用)
        /// 
        /// 使用该方法的实例对象
        /// 倍数
        public static void BaiDefineTime(this int value, int time) {
            Console.WriteLine(
                "小白为 int 拓展的 SpeakValue 方法,当前值{0}的{1}倍为{2}", 
                value,
                time, 
                value * time
            );
        }
    }
    
    // 某方法内↓↓↓
    int i = 10;
    i.BaiDefineTime();// "小白为 int 拓展的 SpeakValue 方法,当前值10的6倍为60"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3.12、内部类

    • 在一个类中再声明一个类(比较鸡肋,平常基本不用,外部类更方便)
    • 特点
      • 使用时要用包裹类点出自己
    • 作用
      • 亲密关系的表现
    • 注意
      • 访问修饰符作用巨大
    class Person 
    {
        public string name;
        public int age;
        public Body body;
    
        // 内部类
        public class Body 
        {
            Arm leftArm;
            Arm rightArm;
    
            // 内部类
            class Arm
            { 
    			// ...
            }
        }
    }
    
    
    // 某方法内↓↓↓
    Person p = new Person();
    // 使用可访问的内部类
    Person.Body body = new Person.Body();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.13、分部类 和 分部方法

    3.13.1、分部类

    • 把一个类分成几部分来声明
    • 关键字 partial
    • 作用
      • 分部描述一个类
      • 增加程序拓展性
    • 注意
      • 分部类可写在多个脚本中
      • 分部类的访问修饰符要一致
      • 分部类中不能有重复成员
    partial class Person
    {
        public string Name;
    }
    
    partial class Person
    {
        public int Age;
    
        public void Speak()
        {
            Console.WriteLine("我叫{0},我今年{1}岁了", Name, Age);
        }
    }
    
    // 某方法内↓↓↓
    Person p = new Person();
    p.Name = "666";
    p.Age = 18;
    p.Speak();// "我叫666,我今年18岁了"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.13.2、分部方法

    • 将方法的声明和实现分离(局限性大,了解即可)
    • 特点
      • 不能加访问修饰符,默认私有
      • 只能在分部类中声明
      • 返回值只能是 void
      • 可以有参数,但不能用 out 关键字
  • 相关阅读:
    国家级专新特精“小巨人”「皖仪科技」携手企企通,打造采购数字化平台成功上线
    Golang之手写web框架
    [react] 什么是虚拟dom?虚拟dom比操作原生dom要快吗?虚拟dom是如何转变成真实dom并渲染到页面的?
    【每日OJ —— 225.用队列实现栈(队列)】
    【计算机组成原理】原码 反码 补码 移码
    【TcaplusDB知识库】Tmonitor单机安装指引介绍(二)
    26 docker前后端部署
    中秋《乡村振兴战略下传统村落文化旅游设计》许少辉八月新书——2023学生思乡季辉少许
    Visual Leak Detector内存泄漏检测机制源码剖析
    两日总结十六
  • 原文地址:https://blog.csdn.net/qq_30769437/article/details/128167797