C#核心之面向对象-封装

发布于:2024-05-06 ⋅ 阅读:(21) ⋅ 点赞:(0)

面向对象-封装
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

文章目录

1、类和对象

1、什么是类
具有相同特征、相同行为的一类事物的抽象
类是对象的模板
可以通过类创建出对象
2、类的声明

在namespace中

3、类声明语法
class 类名{
    特征-成员变量
    行为-成员方法
    保护特征-成员属性
    
    构造函数和析构函数
    索引器
    运算符重载
    静态成员
}
4、类声明实例
class Person{
    
}
5、对象(类)
对象是类创建的
类创建的过程称为实例化对象
    类是引用类型,结构体是值类型
6、实例化对象语法
类名 变量名;
类名 变量名 = null;
类名 变量名 = new 类名();
7、实例化对象
Person p; Person P2 = null; //没有分配堆内存
Person p3 = new Person();	//分配了堆内存
Person p4 = new Person();	//p3和p4没有关系

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

1、成员变量
1、声明在类语句块中
2、用来描述对象的特征
3、可以是任意变量类型
4、数量不做限制
5、是否赋值根据需求来定
enmu E_SexType{Man,Woman,}
struct Position{}
class Pet{}
class Person{
    //特征-成员变量
    public string name;
    public int age;
    public E_SexType sex;
    public Person girlFriend; //类可以声明和自己一样的变量类型,这样的成员变量不能实例化
    public Person[] friend;
    public Position pos;
    public Pet pet;
}
2、访问修饰符
public	共有的 外部访问
private	内部访问,默认
protected 内部和子类访问
3、成员变量的使用和初始值
值类型: 数字默认值为0 bool类型 false
引用类型: null
Console.WriteLine(default(int));

3、成员方法

1、成员方法声明
概念
    成员方法用来表现对象行为
    1、声明在类语句块中
    2、是用来描述对象的行为
    3、规则和函数声明规则相同
    4、收到访问修饰符规则影响
    5、返回值参数不做限制
    6、方法数量不做限制
    
class Person{
    public string name;
    public int age;
    public Person[] friends;
    
    //添加新朋友的方法
    public void AddFriend(Person p){
        if(friends == null){
            friends = new Person[] {p};
        }
        else{
            //新建一个数组
            Person[] newFriends = new Person[friends.Length + 1];
            for(int i = 0;i < friends.Length; i++){
                newFriends[i] = friends[i];
            }
            //添加新朋友
            newFfriends[newFriends.Length - 1] = p;
            //地址重定向
            friends = newFriends;
        }
    }
    
    public void Speak(string str){
        Console.WriteLine("{0}说{1}",name,str);
    }
    
    public bool IsAdult(){
    return age >= 18;
	}
    
    
}
2、成员方法的使用
Person p = new Person();
p.name = "魔君";
p.age = 18;
p.Speak("什么");

if(p.IsAdult){
    p.Speak("成年了");
}

//添加新朋友
Person p2 = new Person();
p2.name = "大舅哥";
p2.age = 20;
p.AddFriend(p2);
for(int i =0;i<p.friends.Length; i++){
    Console.WriteLine(p.friends[i].name);
}

4、构造函数和析构函数

1、构造函数
概念
    在实列化对象时,会调用用于初始化的函数
    若不写,默认存在一个无参构造函数

构造函数的写法
    1、没有返回值
    2、函数名必须和类名相同
    3、构造函数可以重载
    4、this代表当前调用该函数的对象

class Person{
    public string name;
    public int age;
    //类中可以声明无参构造函数,结构体不可以
    public Person(){
        name = "魔君";
        age = 18;
    }
    
    //有参构造会顶替掉默认的无参构造
    public Person(int age, string name){
        this.age = age;	//this代表当前成员变量
        this.name = name;
    }
}
Person p = new Person(18,"魔君");
Console.WriteLine(p.age);
2、构造函数特殊写法
可以通过this 重用构造函数代码
访问修饰符 构造函数名(参数列表):this(参数1,参数2...)	
public Person(){}

public Person(int age, string name):this(){ //先调用无参方法,复用代码
        Console.WriteLine("Person两个参数构造函数调用");
}

public Person():this("魔君"){
        Console.WriteLine("Person两个参数构造函数调用");
}

public Person(int age, string name):this(age + 10){
        Console.WriteLine("Person两个参数构造函数调用");
}
3、析构函数
概念
    当引用类型的堆内存被回收时,会调用该函数
基本语法
    ~类名(){}
	~Person(){}
4、垃圾回收机制
垃圾回收,GC(Garbage Collector)
垃圾回收过程时在遍历堆(Heap)上动态分配的所有对象
垃圾就是没有被任何变量,对象引用的内容
    
垃圾回收算法
    引用计数(Reference Counting)
    标记清楚(Mark Sweep)
    标记整理(Mark Compact)
    复制集合(Copy Collection)
    
GC只负责堆(Heap)内存的垃圾回收,引用类型通过GC回收
栈(Stack)上的内存是由系统自动管理的,值类型会随生命周期自动释放
    
C#中GC大概原理
0代内存	1代内存	2代内存
代是垃圾回收机制使用的一种算法(分代算法)
新分配的对象都会被配置在第0代内存中
每次分配都可能会进行垃圾回收以释放内存(0代内存满时)
    
在一次内存回收过程开始时,垃圾回收器会认为堆中全是垃圾:
    1、标记对象 从根(静态字段、方法参数)开始检查引用对象,标记后为可达对象,未标记为不可达对象
    2、搬迁对象压缩堆(挂起执行托管代码线程),释放未标记的对象,搬迁可达对象,修改引用地址
    
大对象认为是第二代内存,目的是减少性能损耗,提高性能
不会对大对象进行搬迁压缩 85000字节(83kb)以上为大对象
    
//手动GC方法,用于Loading过场景时调用
    GC.Collect();

5、成员属性

1、成员属性的基本概念
1、用于保护成员变量
2、为成员属性的获取和赋值添加逻辑处理
3.解决访问修饰符的局限性
2、成员属性的基本语法
访问修饰符 属性类型 属性名{get{}set{}}

class Person{
    private string name;
    private int age;
    private int money;
    private bool sex;
    
	public string Name{
    	get{
        	return name; //返回通过属性获取的内容
    	}
    	set{
        	//value 关键字用于表示外部传入的值
            name = value;
    	}
	}
}
3、成员属性的使用
Person p = new Person();
p.Name = "老哥";
Console.WriteLine(p.Name);
4、成员属性中 get 、set 前可以加访问修饰符
1、不加修饰符 默认为属性声明时的访问权限
2、加的访问修饰符要低于属性的访问权限
3、不能让get和set的访问权限都低于属性的权限
public int Money{
    get{
        //解密处理
        return money - 5;
    }
    set{
        //加密处理
        money = value + 5;
    }
}
5、get和set可以只有一个

只有一个时,不加访问修饰符

6、自动属性
作用:外部能得不能改的特征
	如果类中有一个特征是只希望外部能得到但不能修改,没有什么特殊处理的情况,可以直接使用自动属性
public float Height{
    get;
    private set;
}

6、索引器

1、索引器基本概念

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

2、索引器语法
访问修饰符 返回值 this[参数类型 参数名, 参数类型 参数名]
{
    内部写法和规则和属性相同
    get{}set{}
}

class Person{
    private string name;
    private int age;
    private Person[] friends;
    
    public Person this[int index]{
        get{
            return fridents[index];
        }
        set{
            friends[index] = value;
        }
    }
}
3、索引器的使用
Person p = new Person();
p[0] = new Person();
Console.WriteLine(p[0]);
4、索引器可以写逻辑
class Person{
    private string name;
    private int age;
    private Person[] friends;
    
    public Person this[int index]{
        get{
            if(fridens == null || friends.Length - 1 < index){
                return null;
            }
            return fridents[index];
        }
        set{
            if(friends == null){
                friends = new Person[] {value};
            }
            else if(index > friends.Length - 1){
                friends[friends.Length - 1] = value;
            }
            friends[index] = value;
        }
    }
}
5、索引器可以重载
p[0,0] = 10;

class Person{
    private string name;
    private int age;
    private Person[] friends;
    
    private int[,] array;
    
    public int this[int i, int j]{
        get{
            return array[i,j];
        }
        set{
            array[i,j] = value;
        }
    }
    
    public string this[string str]{
        get{
            switch(str){
                case "name":
                        return this.name;
                case "age":
                    return age.ToString();
            }
            return "";
        }
    }
    
    public Person this[int index]{get{}set{}}
}
思考 封装增删改查
//定义一个整型数组类,该类中有一个整型数组变量,为它封装增删改查方法

IntArray array = new IntArray();
array.Add(10);
array.Add(20);
array.Add(30);
array.Add(40);
array.Add(50);
//array.RemoveAt(1);
array.Remove(2);
Console.WriteLine(array[3]);
Console.WriteLine(array.Length);
class IntArray
{
    private int[] array;
    //房间容量
    private int capacity;
    //当前房间数
    private int length;
    public IntArray()
    {
        capacity = 5;
        length = 0;
        array = new int[capacity];
    }
    //增
    public void Add(int value)
    {
        //是否扩容
        if(length < capacity)
        {
            array[length] = value;
            length++;
        }
        else  // 扩容,换新数组
        {
            capacity *= 2;
            //新房间
            int[] tempArray =new int[capacity];
            //旧换新
            for (int i = 0; i < array.Length; i++)
            {
                tempArray[i] = array[i];
            }
            array = tempArray;  //老房子地址指向新房子地址
            //添加内容
            array[length] = value;
            length++;
        }
    }
    //删
    public void Remove(int value) 
    {
        for (int i = 0; length > i; i++)
        {
            if (array[i] == value)
            {
                RemoveAt(i);
                return;
            }
        }
        Console.WriteLine("没有在数组中找到{0}",value);
    }

    public void RemoveAt(int index)
    {
        if (index > length - 1)
        {
            Console.WriteLine("当前数组长度只有{0}", length);
            return;
        }
        for (int i = index; i < length - 1; i++)
        {
            array[i] = array[i + 1]; //前移数组元素
        }
        length--;
    }
    //改查
    public int this[int index]
    {
        get
        {
            if(index>=length || index < 0)
            {
                Console.WriteLine("越界");
                return 0;
            }
            return array[index];
        }
        set
        {
            if (index >= length || index < 0)
            {
                Console.WriteLine("越界");
            }
            array[index] = value;
        }
    }

    public int Length
    {
        get
        {
            return length;
        }
    }
}

7、静态成员

1、静态成员概念
静态关键字 staticstatic修饰的 成员变量、方法、属性等,称为静态成员
静态成员的特点:直接用类名.静态成员使用
2、遇见的静态成员

Console是静态成员

3、自定义静态成员
class Test{
    //静态成员变量
    public static float PI = 3.14f;
    //成员变量
    public int testInt = 100;
    //静态成员方法
    public static float CalCircle(float r){
        return PI * r * r;
    }
    //成员方法
    public void TestTun(){
        Console.WriteLine("123");
    }
}
4、静态成员的使用
Console.WriteLine(Test.PI);
Console.WriteLine(Test.CalCircle(2));
5、为什么可以直接使用
静态成员的特点
    程序开始运行时,就会分配内存空间。所以静态成员和程序同生共死,静态成员具有唯一性和全局性
6、静态函数中不能使用非静态成员

成员变量只能将对象实例化后才能使用,不能直接使用 非静态成员

7、非静态函数可以使用静态成员

静态生命周期长

8、静态成员对于我们的作用
静态变量:
    1、常用唯一变量的声明
    2、方便别人获取对象声明
静态方法:
    常用唯一的方法声明
9、常量和静态常量
const 可以理解为特殊的static
相同点:
    都可以不通过实例化直接使用
不同点:
    1const必须初始化,不能修改; static没有此规则
    2const只能修饰变量; static可以修饰其他
    3const一定写在访问修饰符后; static可以写前面
思考 单例模式思想
//一个类对象,在整个应用程序的生命周期中,有且仅有一个该对象存在
//不能在外部实例化,直接通过该类类名就能够得到唯一的对象

Console.WriteLine(Test.T.testInt);

class Test
{
    private static Test t = new Test();
    public int testInt = 10;
    public static Test T
    {
        get
        {
            return t;
        }
    }
    private Test() 
    {
        
    }
}

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

1、静态类
概念
	用static修饰的类
特点
	只能包含静态成员,不能被实例化
作用
	1、将常用的静态成员写在静态类中,方便使用
	2、静态类不能被实例化,更能体现工具类的唯一性
2、静态构造函数
概念
    在构造函数上加static修饰
特点
    1、静态类和普通类都可以
    2、不能使用访问修饰符
    3、不能有参数
    4、只会调用一次
作用
    在静态构造函数中初始化 静态变量
使用
    1、静态类中的静态构造函数
    2、普通类中的静态构造函数
思考 写一个静态的工具类
//写一个用于数学计算的静态类
//该类中提供计算圆面积,周长,矩形面积,矩形周长,取一个数的绝对值

Console.WriteLine(MathTool.CalcCircularArea(2));
Console.WriteLine(MathTool.CalcCircularLength(2));
Console.WriteLine(MathTool.CalcRectArea(2,3));
Console.WriteLine(MathTool.CalcRectLength(2,3));
Console.WriteLine(MathTool.GetABS(-22));

static class MathTool
{
    public static float PI = 3.14f;
    /// <summary>
    /// 计算圆面积
    /// </summary>
    /// <param name="r">半径</param>
    /// <returns>周长</returns>
    public static float CalcCircularArea(float r)
    {
        return PI * r * r;
    }
    public static float CalcCircularLength(float r)
    {
        return 2 * PI * r;
    }
    public static float CalcRectArea(float w,float h)
    {
        return w * h;
    }
    public static float CalcRectLength(float w, float h)
    {
        return 2 * (w + h);
    }

    public static float GetABS(float value)
    {
        if (value < 0)
        {
            return -value;
        }
        return value;
    }
}

9、拓展方法

1、拓展方法的基本概念
概念
    为现有非静态变量类型添加新方法
作用
    1、提升程序拓展性
    2、不需要再对象中重新写方法
    3、不需要继承来添加方法
    4、为别人封装的类型写额外的方法
特点
    1、一定是写在静态类中
    2、一定是一个静态函数
    3、第一个参数为拓展目标
    4、第一个参数用this修饰
2、基本语法
访问修饰符 static 返回值(this 拓展类名 参数名,参数类型 参数名) 
3、示例
int i = 10;
i.SperkValue();

static class Tools{
    //为int拓展了一个成员方法
    //成员方法是需要实例化对象后才能使用
    //value代表使用该方法的实例化对象
    public static void SpeakValue(this int value){
        //拓展方法的逻辑
        Console.WriteLine("为int拓展的方法"+value);
    }
}
4、为自定义的类型拓展方法
Test t = new Test();
t.Fun3();
static class Tools{
    public static void Fun3(this Test t){
        Console.WriteLine("为Test写的拓展方法");
    }
}
class Test{
    public int i = 20;
    public void Fun1(){
        Console.WriteLine("123");
    }
    public void Fun2(){
        Console.WriteLine("456");
    }
}

10、运算符重载

1、基本概念
概念
    让自定义类和结构体能够使用运算符
关键字
    operator
特点
    1、一定是一个公共的静态方法
    2、返回值写在operator前
    3、逻辑处理自定义
作用
    让自定义类和结构体对象可以进行运算
注意
    1、条件运算符需要成对实现
    2、一个符号可以多个重载,注意参数
    3、不能使用ref 和 out
2、基本语法
public static 返回类型 operator 运算符(参数列表)
3、示例
Point p = new Point();
p.x = 1;
p.y = 1;
Point p2 = new Point();
p2.x = 2;
p2.y = 2;
Point p3 = p + p2;

Point p4 = p3 + 2;

class Point{
    public int x;
    public int y;
    
    public static Point operator +(Point p1, Point p2){
        Point 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){
        Point p = new Point();
        p.x = p1.x + value;
        p.y = p1.y + value;
        return p;
    }
}
4、可重载和不可重载的运算符
1、可重载的运算符
    算数运算符	//都可以
    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, Point p2){return null};
	public static Point operator >>(Point p1, Point p2){return null};
    条件运算符	//成对出现
    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};
2、不可重载的运算符
    逻辑与&& 逻辑或||
    索引符[]
    强转运算符()
    特殊运算符
    	三目运算符  赋值符号=.
思考 重载运算符实现Vector3类(x,y,z)
//定义一个Vector3类(x,y,z)通过重载运算符实现以下运算
//(x1, y1, z1) + (x2, y2, z2) = (x1 + x2, y1 + y2, z1 + z2)
//(x1, y1, z1) - (x2, y2, z2) = (x1 - x2, y1 - y2, z1 - z2)
//(x1, y1, z1) * num = (x1 * num, y1 * num, z1 * num)

Vector3 v1 = new Vector3(1,1,1);
Vector3 v2 = new Vector3(2,2,2);

Vector3 v3 = v1 + v2;
Console.WriteLine("Vector3({0},{1},{2})", v3.x,v3.y,v3.z);

Vector3 v4 = v1 - v2;
Console.WriteLine("Vector3({0},{1},{2})", v4.x, v4.y, v4.z);

v4 *= 3;
Console.WriteLine("Vector3({0},{1},{2})", v4.x, v4.y, v4.z);

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 static Vector3 operator +(Vector3 v1, Vector3 v2)
    {
        return new Vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
    }
    public static Vector3 operator -(Vector3 v1, Vector3 v2)
    {
        return new Vector3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
    }
    public static Vector3 operator *(Vector3 v,int num)
    {
        return new Vector3(v.x * num, v.y * num, v.z * num);
    }
}

11、内部类和分部类

1、内部类

概念
    在类中再声明一个类
特点
    使用时要用包括者点出自己
作用
    亲密关系的体现
注意
    访问修饰符作用很大

2、分部类

概念
    把一个类分成几部分声明
关键字
    partial
作用
    分部表述一个类
	增加程序的拓展性
注意
    分部类可以写在多个脚本文件中
    分部类的访问修饰符要一致
    分部类中不能有重复成员

3、分部方法

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