C#面向对象编程基础

发布于:2023-01-01 ⋅ 阅读:(285) ⋅ 点赞:(0)

@作者: 风不停息丶
在这里插入图片描述


💻什么是面向对象编程?

  • 面向对象是一种对现实世界理解和抽象的编程方法把相关的数据和方法组织为一个整体来看待、从更高的层次来进行程序开发、更贴近事物的自然运行模式
  • 提高代码复用率、提高开发效率、提高程序可拓展性、清晰的逻辑关系
  • 三大特性:封装+继承+多态
  • 七大原则:开闭原则、依赖倒转原则、里氏替换原则、单一职责原则接口隔离原则、合成复用原则、迪米特法则

面向对象编程——👉封装

  • 用程序语言来形容对象。

类和对象

类和对象具相同特征,相同行为,一类事物的抽象,类是对象的模板,可以通过类创建对象。
类和对象都是引用类型的

class 类名
{
    //特征——成员变量
    //行为——成员方法
    //保护特征——成员属性
    
    //构造函数和析构函数
    //索引器
    //运算符重载
    //静态成员
}
  • 实例化对象的基本语法
namespace Les
{
    class Person
    {
        
    }
    class Program
    {
        static void Main(string[] args)
        {
            //类名 变量名; 			 (没用分配堆内存)
            //类名 变量名 = null;	 (没用分配堆内存)
            //类名 变量名 = new 类名;	(在堆中新开了个房间)
            Person p ;
            Person p2 = null;
            Person p3 = new Person; 
        }
    }
}

成员变量和访问修饰符

  • 成员变量

用于描述对象的特征,可以是任意变量类型(枚举,结构体,类对象),是否赋值根据需求而定
如果要声明一个和自己相同类型的成员变量时,不能对他进行实例化(会死循环!!)

class Person
{
    public int a;
    public float b;
    public char c;
    public bool d;
}
Person r = new Person();
int a =r. a;
  • 访问修饰符

public: 公开的,所有对象都能访问和使用
private: 私有的,只有自己内部才能访问和使用, 变量前不写默认为该状态
protected: 只有自己内部和子类才能访问和使用,继承的时候用到

	public 	int a;
    private int b;
    protected int c;

成员方法

和结构体中函数的声明使用差不多,用来描述对象的行为,在类中声明
受访问修饰符的影响,不需要加static关键字,成员方法需要实例化才能使用

//Person类中自动扩容的数组的方法
class Person
{
    //特征——成员变量
    public bool sex;
    public string nanme;
    public float high;
    public int age;
    public Person[] friend;
    
    //行为——成员方法
    /// <summary>
    /// 扩容friend数组
    /// </summary>
    /// <param name="p"></param>
    public void AddFriend(Person p)
    {
    	//如果为空,实例化一个新的friend 
        if (friend ==null)
        {
            friend = new Person[] { p };
        }
        else
        {
            //数组扩容+1
            Person[] newFriend = new Person[friend.Length + 1];
            //遍历赋值新数组
            for (int i = 0; i < friend.Length; i++)
            {
                newFriend[i] = friend[i];
            }          
            //将新成员p存在新数组的最后一个索引
            newFriend[newFriend.Length - 1] = p;
        }
    }
}

构造函数和析构函数

  • 构造函数

在实例化对象时会调用的用于初始化的函数,如果不写就默认存在一个无参构造函数
和结构体中构造函数的写法一致,(类允许自己申明一个无参构造函数 结构体是不允许的)
无返回值,函数名和类名必须相同,一般都是public,可以重载构造函数
声明有参构造函数之前最好一起声明一个无参构造函数,声明有参构造时默认的无参构造就不存在了,要手动声明

class Person
{
    //特征——成员变量
    public bool sex;
    public string nanme;
    public float high;
    public int age;
    public Person[] friend;
        
    //构造函数 实现对象实例化时 初始化
    //构造函数可以重载
    
    //无参构造函数
    public Person()
    {
        nanme = "小明";
        age = 20;
        sex = true;
        high = 180;
    }
    
    //有参构造函数
    //如果在构造函数后添加 :this(指定的重载参数) this指定了先执行无参 
    public Person(string name, int age,bool sex)//:this()
    {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }       
}
  • 析构函数

当引用类型的堆内存被回收时,会调用该函数
对于需要手动管理内存的语言(比如c++),需要在析构函数中做一些内存回收处理
c#中有自动垃圾回收机制gs,所以几乎不使用析构函数

class Person
{
     ~Person()
    {
    
    }
}

成员属性

用于保护成员变量,为成员属性的获取和赋值添加逻辑处理
get , set可以写一个(起到保护作用)

private string name;
public string Name
{
  get
  {
       //可以在返回之前添加逻辑规则
       //意味着这个属性可以获取的内容
       return name;
  }
  set
  {
      //可以在设置前添加逻辑规则
      // value 关键字 用于表示外部传入的值
      name = value;
  }
}
  • 数值保护和加密处理

get 和 set 前可加访问修饰符
加的访问修饰符要低于属性的访问权限,比如:默认权限是private那 get 和 set 不能添加public
不能让get和set的访问权限都低于属性的权限,get 和 set不能同时添加private 或同时添加public

public float Money
        {
            get
            {
                //可以在返回之前添加逻辑规则
                //意味着这个属性可以获取的内容
                
                //解密处理
                return Money - 5;
            }
            set
            {
                //可以在设置前添加逻辑规则
                if (Money< 0)
                {
                    Money= 0;
                    Console.WriteLine("钱不能小于零");
                }

                // value 关键字 用于表示外部传入的值
                //加密处理
                Money = value + 5;
            }
        }
  • 自动属性(自动的成员变量),外部能得不能改的特征,很少使用
  • 没有再 get 和 set 中写逻辑的需求或者想法
public int Money{get;private set;}
  • 成员属性总结

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


索引器

作用:可以以中括号的形式范围自定义类中的元素 规则自定义 访问时和数组一样,适用于在类中有数组变量时使用
索引器可以重载
如果功能和成员属性相同,可以不写

class Person
{
    public name;
    public age;
    public Person friends[];
        
    public Person this[int index]
    {
        get
        {
			//可以写逻辑的根据需求来处理这里面的内容
            return friends[index];
        }
        set 
        {
        //自己定了一个规则如果索引越界就默认把最后一个顶替
            if (friends==null)
            {
                friends = new Person[]{value};
            }
            friends[index] = value
        }
    }
}

---------------------------------------
//调用
Person p = new Person();
p[0] = new Person;

静态成员

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

注意:
1.静态函数中不能直接使用非静态成员
2.非静态函数中可以直接使用静态成员

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

  • 静态方法计算圆的面积
class MyCalss
{
    public static float PI = 3.1415926f;
    
    public static float AreaOfCircle (float r)
    {
        return Person.PI * r * r;
    }
}

//使用**********************************************
float areaCircle =  MyCalss.AreaOfCircle(7);

静态类和静态构造函数

  • 静态类

使用static关键字修饰的类
只能包含静态成员不能被实例化
具有唯一性 适合用作工具类(计算公式 等)

  • 静态构造函数

用static修饰的构造函数
特点 :
1.静态类和普通类都可以有静态构造函数
2.不能使用访问修饰符
3. 不能有参数,只会自动调用一次
作用 :初始化静态成员

  • 静态类中的构造函数
    static class StaticClass
    {
        public static int testInt = 100;
        public static int testInt2 = 200;

        static StaticClass()
        {
            Console.WriteLine("静态构造函数");
            testInt = 200;
            testInt2 = 300;
        }
    }
//**************************************
//控制台会先输出static StaticClass()
Console.WriteLine(StaticClass.testInt);//输出200
Console.WriteLine(StaticClass.testInt2);//输出300
  • 普通类中的构造函数
class Test
    {
        public static int testInt = 200;
        static Test()
        {
            Console.WriteLine("静态构造");
        }
        public Test()
        {
            Console.WriteLine("普通构造");
        }
    }
    //*************************************
    //先输出static Test()
    Console.WriteLine(Test.testInt);//
    Test t = new Test();//200

拓展方法

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

  • 为自带int拓展了一个成员方法
    
static class Class1
    {
        //成员方法是需要实例化对象后才能使用的
        //value代表使用该方法的实例化对象
        public static void SpeakValue(this int value)
        {
            //拓展的方法 对逻辑
            Console.WriteLine("为int拓展的方法" + value);
        }
    }
//***********************************************调用
int i = 10;
i.SpeakValue();//输出为int拓展的方法10
  • 为自定义方法拓展方法
    
public class Tools
{
    public void Fun()
    {
        Console.WriteLine("方法1");
    }

    public void Fun2()
    {
        Console.WriteLine("方法2");
    }
}

public static class TuoZhan
{
    public static void Fun3(this Tools2 value ,int a)
    {
        Console.WriteLine("方法3" + a);
    }   
}

//***************************************************
Tools t = new Tools();
t.fun3(2);//输出:方法32
  • 总结
    在这里插入图片描述

运算符重载

不常用,了解即可
关键词:operator
实现自定义类型的运算
可以多个重载
参数的数量 和运算符对应 (++ 单个参数 * 两个参数)
提升程序的扩展性

  • 基本语法
    public static 返回类型 operator 运算符(参数列表)
//运算符重载 实现自定义类型的运算
public static Person operator +(Person p1, Person p2)
{
    Person p = new Person();
    p.age = p1.age + p2.age;
    p.high = p1.high + p2.high;
    return p;
}
//***************************************************
Person p2 = p + p1;//输出
  • 可重载的运算符
    算数运算符: + - * / % ++ –
    逻辑运算符: !
    位运算符: & | ^ ~ << >>
    条件运算符: < <= > >= == !=
  • 不可重载的 运算符
    逻辑运算符 : && || [ ] () . = ?:

内部类和分部类

不常用,了解即可

  • 内部类
    在类中再声明一个类 亲密关系的体现
class Person
{
    //人
    public int age;
    public string name;
    public Body body;
    
    public class Body
    {
        //身体
        Arm leftArm;
        public class Arm
        {
            //手臂
        }
    }
}
  • 分布类
    关键字:partial
partial class Student
{
    public bool sex;
    public string name;
}
partial class Student
{
    public int number;
    public float high;
}
  • 分布方法
    在这里插入图片描述
    在这里插入图片描述

面向对象编程——👉继承

复用封装对象的代码;儿子继承父亲,复用现成代码。

基本概念

在这里插入图片描述
在这里插入图片描述

  • 基本语法
class 类名 : 被继承的类名
{
    
}
class Teacher
    {
        //名字
        public string name;
        //工号
        public int number;
        //介绍名字
        public void SpeakName()
        {
            Console.WriteLine(name);
        }
    }

    //继承Teacher类
    class TeachingTeacher : Teacher
    {
        //科目
        public string subject;
        //介绍科目
        public void SpeakSubject()
        {
            Console.WriteLine(subject+"老师");
        }
    }

  • 访问修饰符的影响
    public -公共内外部访问
    private -私有内部访问
    protectec -保护内部和 子类能访问
    C#中允许子类存在和父类同名的成员但是极不建议使用
  • 总结
    在这里插入图片描述

里氏替换原则

父类容器转载子类对象、方便进行对象存储和管理
在这里插入图片描述

  • 基本实现
class GameObject
{
    
}

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

class Moster : GameObject
{
    public void MosterAtk()
    {
        Console.WriteLine("怪物攻击");
    }
}

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

//****************************************************
//里氏替换原则,用父类容器装载子类对象
//但是Player类的功能无法使用要用is as转换
GameObject player = new Player();

//✅is和as
//is判断一个对象是否是指定的对象
//返回值bool
if (player is Player)
{
    //as:将一个对象转换为指定类对象 
    //返回值:指定类型对象
    //成功返回指定类对象 失败返回null
    Player p = player as Player;
}

//可以正常使用Player类的功能了
p.PlayerAtk();

//*******************************************************

//实际使用时和数组配合使用多 方便进行对象存储和管理
GameObject[] objects = new GameObject[] { new Player(), new Moster(), new Boss() };

//遍历objects数组 来判断类和执行类
for (int i = 0; i < objects.Length; i++)
{
    if (objects[i] is Player)
    {
        (player as Player).PlayerAtk();
    }
    else if (objects[i] is Moster)
    {
        (player as Moster).MosterAtk();
    }
    else if (objects[i] is Boss)
    {
        (player as Boss).BossAtk();
    }
}

在这里插入图片描述


继承中的构函数

先执行父类的构造函数再执行子类构造函数
子类实例化时 默认调用无参 父类没有无参构造就会报错

  • 1.始终保持申明一个无参构造
    2.通过base调用指定父类构造 (注意和this的区别)
class Father 
{
    int a ;
    public Father (int a)
    {
        this a = a;
    }
}

class Son : Father
{
    public Son (int a) : base(a)
    {
        
    }
}

//*******************************调用
Son s = new Son(1);

Object和装箱拆箱

  • 万物之父Object是所有类的基类
    作用:
    1、可以利用里氏替换原则,用object容器装所有对象
    2、可以用来表示不确定类型,作为函数参数类型
  • 使用方法:引用类型和里氏替换原则—样,用is和as值类型使用括号强转
class Test
{
    public Speak()
    {
        Console.WriteLine("小明");
    }
}
//**************************
//装引用类型 和使用 ——里氏替换
object o = new Test();
if(o is Test)
{
    (o as Test).Speak;
}
//装值类型 和使用 ——括号强转
object o2 = 10;
int a = (int)o2;
//装特殊类型 string
object o3 = "你好呀";
string str = o3.ToString;//也可以使用引用类型的 o3 as string
//装特殊类型 数组
object o4 = new int[10];
int[] arr = (int[])o4;//也可以使用引用类型的 o4 as int[]
  • 装箱拆箱

值类型和object之间发生的
装箱:把值类型用引用类型存储 栈内存移到堆内存中
拆箱:把引用类型存储的值类型取出来 堆内存移到栈内存中
好处:不确定类型时可以方便参数的存储和传递
坏处:存在内存迁移,增加性能消耗

int a =10;
//装箱
object o = a;
//拆箱
int b = (int)o;
  • 密封类sealed
    1、作用是让类无法被继承
    2、保证程序的规范性,安全性
    3、意义:加强面向对象程序设计的规范性、结构性、安全性
sealed class Father 
{
    
}


面向对象编程——👉多态

同样行为的不同表现,儿子继承父亲的基因但是有不同的行为表现。

多态VOB基本概念

  • virtual(虚函数) override(重写) base(父类)
  • 在执行同一方法时有不同的表现
    请添加图片描述
class GameObject
    {
        //虚函数 virtual
        public virtual void Atk()
        {
            Console.WriteLine("游戏对象进行攻击");
        }
    }

    class Player : GameObject
    {
        //重写
        public override void Atk()
        {
            //base可以保留父类的方法
            //base.Atk();
            Console.WriteLine("玩家对象进行攻击");
        }
    }

    class Monster : GameObject
    {
        //重写
        public override void Atk()
        {
            Console.WriteLine("怪物进行攻击");
        }
    }

调用

internal class Program
    {
        static void Main(string[] args)
        {
            GameObject p = new Player();
            p.Atk();//输出玩家对象进行攻击
            GameObject n = new Monster();
            n.Atk();//输出怪物进行攻击
        }
    }

请添加图片描述


抽象类和抽象方法

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

如何选择普通类还是抽象类
不希望实例化的对象 相对抽象的类可以使用 如 :人person 水果fruit
父类中的行为不太需要被实现 只希望子类去定义具体的规则
整体框架设计时会使用 让基类更安全

abstract class Fruits
{
    
}
  • 抽象方法
    又叫纯虚方法,关键词:abstract

特点:
1.只能在抽象类中声明
2.没有方法体
3.不能是私有的
4.继承后必须要override重写

abstract class Fruits
{
    public string name;
    public abstract  void Bad ();
}

class Apple : Fruits
{
    public override void Bad ()
    {
        Console.WriteLine("苹果坏了");
    }
}
//****************************************
//遵循里氏替换 父类装子类
Fruits f = new Apple();
f.Bad();

请添加图片描述

  • 虚方法(vritual)和纯虚方法(abstract)的区别
    虚方法可以在抽象类和非抽象类中声明 纯虚方法只能在抽象类中声明
    虚方法可以由子类选择性实现 纯虚方法必须实现重写。虚方法有方法体可实现逻辑
    他们都可以被子类用override重写 可以多层重写 子子类重写子类重写父类

接口

  • 基本概念
    请添加图片描述
  • 语法
    接口关键词:interface
interface IFly
{
    //方法
    void Fly();
    
    //属性
    string Name
    {
        get;
        set;
    }
    
    //索引器
    int this[int index]
    {
        get;
        set;
    }
    
    //事件 c#进阶讲
    event Action doSomthing;
}
  • 接口可以继承接口
    相当于行为合并
    接口继承接口时 不需要实现
    待类继承接口后 类去实现所有内容
interface Iwalk
        {
            void Walk();
        }
        interface IMove : Iwalk
        {

        }
  • 显示实现接口
    不常用
    当一个类继承两个接口,但是接口中存在着同名方法时
    注意:显示实现接口时不能写访问修饰符
class Player : Iwalk, IMove
        {
            //显示接口 就是用 接口名.行为名 去实现
            void  Iwalk.Walk()
            {
                throw new NotImplementedException();
            }
        }
  • 接口的作用和总结
  • 作用
    抽象行为
    把行为抽象出来供子类继承。个别功能独立在基类外,子类需要时继承。
    提高程序复用性
  • 总结
    继承类:是对象间的继承,包括特征行为等。
    接口继承:是行为间的继承,继承接口的行为规范,按照规范去实现内容。
    接口也是遵循里氏替换,所以可以用接口容器装对象,
    实现装载毫无关系但是却有相同行为的对象。
    接口包含:成员方法,属性,索引器,事件,且都不实现 都没有访问修饰符
    接口继承接口相当于行为合并

密封方法

不太重要
关键字:sealed 修饰重写函数
让虚方法或者抽象方法不能再被重写
和 override 一同出现
请添加图片描述


💻结尾总结

  • 这基本上就是面向对象基础的全部内容了,看到这篇文章的同志们可以点个赞👍支持一下,谢谢!!!

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到