• C#语言基础


    C#语言基础

    1.编程语言介绍

    一些主流编程语言

    • 1.C:嵌入式硬件开发,操作系统底层
    • 2.C++:游戏客户端,服务器,软件
    • 3.C#:游戏客户端,服务器,软件,网站
    • 4.Java:安卓,服务器,软件,网站
    • 5.JavaScript:网站,服务器
    • 6.PHP:网站,服务器
    • 7.Python:爬虫,AI,机器学习,数据分析,自动化运维测试…
    • 8.SQL:数据库
    • 9.Go:服务器
    • 10.Objective-C:mac,ios开发
    • 11.Swift:mac,ios开发

    使用IDE:Visual Studio2019 Community

    2.C# 基础语法

    1.输入输出

    //输出后不跳行
    Console.Write("xxx");
    //输出后跳行
    Console.WriteLine("xxx");
    
    //检测玩家的一键输入
    Console.ReadKey("xxx");
    //检测玩家的一系列输入
    Console.ReadLine("xxx");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.变量

    折叠代码

    //折叠代码
    #region myRegion
    
    #endregion
    
    • 1
    • 2
    • 3
    • 4

    变量:可以变化的容器
    语法变量类型 变量名 = 初始值;
    一些常用变量类型:
    有符号的整形变量

    • 1.sbyte:-128~127
    • 2.int:-21亿~21亿多
    • 3.short:-32768~32767
    • 4.long:-900w兆~900w兆
      无符号的整形变量
    • 5.byte:0~255
    • 6.uint:0~42亿
    • 7.ushort:0~65535
    • 8.ulong:0~1800w兆
      浮点数
    • 9.float:存储7/8位有效数字
    • 10.double:存储15~17位有效数字,抛弃的数字会 四舍五入
    • 11.decimal:存储27~28位有效数字
      特殊类型
    • 12.bool:表示真假的数据类型
    • 13.char:存储单个字符的变量类型
    • 14.string:字符串,用来存储多个字符,无上限
    //声明整形变量
    int i = 666;
    //声明浮点变量
    float f = 0.11111111f;
    //声明decimal数据
    decimal de = 0.12345678910111235548m;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    变量的本质
    变量中的存储单位所占字节数:(单位:字节byte)

    • sbyte,byte,bool:1
    • int,uint,float:4
    • short,ushort,char:2
    • long,ulong,double:8
    • decimal:16

    变量的命名
    四不能:

    • 1.不能重名
    • 2.不能以数字开头
    • 3.不能以程序关键字命名
    • 4.不能有特殊符号(下划线除外)

    二规范:

    • 1.驼峰命名法:首字母小写,之后单词首字母大写(变量)
    • 2.帕斯卡命名法:所有单词首字母都大写(函数,类)

    常量
    语法:const 变量类型 变量名 = 初始值
    特点:

    • 1.必须初始化
    • 2.不能被修改

    作用:声明一些不变的变量。

    const float PI = 3.1415926f;
    
    • 1

    转义字符
    语法:\'

    //定义字符串中有特殊字符
    string str = "\'哈哈哈\'"; 
    Console.WriteLine(str); //'哈哈哈'
    
    • 1
    • 2
    • 3

    \n:换行
    \\:单斜杠
    \t:输出一个制表符
    \b:光标后退一格
    \0:空
    \a:警告音


    3.变量类型转换

    类型转换

    隐式转换:不同类型之间自动转换(大范围装小范围高精度存低精度
    注意,decimal不适用隐式转换原则。bool,string,char不存在隐式转换。

    相同大类型转换:

    //隐式转换
    long l = 1;
    int i = 1;
    l = i;
    
    • 1
    • 2
    • 3
    • 4

    不同大类型转换:
    无符号无法装有符号数字。
    有符号装无符号数字同需要符合范围完全覆盖原则。

    浮点数可以转任何类型整数。
    decimal不能隐式转换为float 和 double
    但它可以存储整形。

    char类型可以隐式转换为整形或浮点型,其转换后形成的是字符的 ASCII码。

    显示转换

    • 1.括号强转
      可能会出现范围问题造成的异常!
      浮点数转整数时,会直接抛弃掉小数部分。
      bool和string无法通过括号强转。
    short s = 1;
    int i = 1;
    s = (short)i;
    
    • 1
    • 2
    • 3
    • 2.Parse强转
    • 考虑转换后的范围是否能够被新类型存储。
    //将字符串转换成int
    int i = int.Parse("123");
    
    • 1
    • 2
    • 3.Convert强转
      作用:更准确的将各个类型相互转换,其精度比括号强转高,遵循四舍五入。
    int a = Convert.ToInt32("12");
    int a = Convert.ToInt32("1.4999");//1
    int a = Convert.ToInt32(true);//1
    short s5 = Convert.ToInt16("1");
    long l5 = Convert.ToInt64("1");
    
    float f = Convert.ToSingle("13.2");
    bool bo = Convert.ToBoolean("true");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 4.ToString强转
      语法:变量.ToString()
      转字符串还可以使用字符串拼接。

    4.异常捕获

    作用:
    基本语法:try{ }catch{ }finally{ }

    try
    {
    	//希望进行异常捕获的代码块
    	//如果报错,则执行catch语块	
    }catch(Exception e)
    {
    	//捕获异常(打印)
    }
    finally
    {
    	//最后执行的代码,不管有没有出错,都会执行其中的代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    5.运算符

    6.条件语句与循环语句

    7.控制台,随机数以及调试

    控制台补充API:

    //读取输入但不在控制台显示
    char c = Console.ReadKey(true).KeyChar;
    
    //清空控制台内容
    Console.Clear();
    
    //关闭控制台
    Environment.Exit();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    随机数相关API

    //产生随机数对象
    Random random = new Random();
    //生成随机数
    int i = r.Next();//生成一个非负随机数
    i = r.Next(100); // 生成一个0~99的随机数
    
    • 1
    • 2
    • 3
    • 4
    • 5

    8.枚举,数组

    9.值类型和引用类型

    值类型:其他,结构体
    引用类型:string,数组,类

    ref 和 out区别:

    • 1.ref传入的变量必须初始化,但在内部可改可不改
    • 2.out传入变量不用初始化,但在内部必须修改该值

    作用:解决值类型和引用类型在函数内部 改变值 或者 重新声明能够影响外面传入的变量,让其也更改。

    变长参数关键字params
    传入的参数都会参数在arr数组中,传入多少数字都不会报错。
    params修饰的参数必须放在参数列表的最后位置且只能有一个。
    用法示例

    int Sum(params int[] arr)
    {
    	
    }
    
    //调用求和函数
    int res = Sum();
    int res1 = Sum(1,2,3,4,6,5);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    10.OOP特性之封装

    重点内容

    • 1.当在一个Person类中的属性出现同类时,无法直接在类内部赋值,会报 栈溢出异常
    class Person 
    {
    	Person p = new Person(); //x
    
    	//构造函数的复用
    	public Person(int age){}
    	public Person(int age,string name):this(age){
    		//自动调用上面的age参数的函数
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 2.析构函数:用于需要手动释放内存的语言(cpp),c#存在垃圾回收机制。垃圾回收的时候调用析构函数。
    ~Person(){}
    
    • 1

    垃圾回收机制(GC):识别哪些 没有被任何变量,对象引用的内容,会被回收释放。常见相关算法有:引用计数,标记清除,标记整理,复制集合GC只负责堆(Heap)中内存的垃圾回收。而栈上的内存由系统自动管理,有自己的生命周期,会自动分配与释放。
    c#中的垃圾回收机制详解
    分代算法:内存分为0代,1代和2代。新分配的内存都会被分配到0代,每次分配都可能(0代内存满时)会进行垃圾回收以释放内存。一次内存回收过程开始时,垃圾回收会认为堆中全是垃圾,会进行以下两步:

    • 1.标记对象,从根(静态字段,方法参数)开始检查引用对象,标记后为可达对象,未标记为不可达对象。
    • 2.不可达对象被认为是垃圾,搬迁可达对象并压缩堆,并释放未标记的对象,修改可达对象的引用地址。

    此外,大对象(83kb以上)总是被存在二代内存中,目的是 减少内存消耗,提高性能。
    在游戏中,常常在Loading时会被调用,以提高玩家的体验

    //手动触发垃圾回收
    GC.Collect();
    
    • 1
    • 2
    • 3.成员属性
      语法:访问修饰符 属性类型 变量名{get{} set{}}
      注意:set,get语块不加访问修饰符,默认为属性的访问权限。加的访问修饰符权限必须低于属性的访问权限。不能让get和set的访问权限都低于属性权限。
    private string name;
    
    public string Name
    {
    	get
    	{
    		return name;
    	}
    	set
    	{
    		name = value;
    	}
    }
    
    //自动属性(会自动生成一个成员变量height)
    public float Height
    {
    	get;
    	set;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 4.索引器
      概念:让对象可以像数组一样通过索引访问其中元素,程序看起来更加直观,更容易编写。
      索引器可以重载
    class Person
    {
    	private Person[] friends;
    	
    	public Person this[int index]
    	{
    		get
    		{
    			return friends[index];
    		}
    		set
    		{
    			friends[index] = value;
    		}
    	}
    }
    
    class Person
    {
    	public  int this[int i,int j]{
    		get;set;
    	}
    }
    
    class Test
    {
    	Person p = new Person();
    	p[0] = new Person();
    }
    
    • 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
    • 5.静态成员与程序同生共死,程序会为其开辟一块独有空间,静态成员中不能使用非静态成员(生命周期的差异性)。全局性与独有性。
    • 6.拓展方法:
      概念:为现有 非静态 变量类型 添加 新方法。
      作用
      – 1.提高程序拓展性
      – 2.不需要再对象中重写方法
      – 3.不需要继承来添加方法
      – 4.为别人封装的类型写额外的方法
      特点
      – 1.一定是写在静态类中
      – 2.一定是个静态函数
      – 3.第一个参数为拓展目标
      – 4.第一个参数用this修饰
      实例
    //定义
    static class Tools
    {
    	public static void speakValue(this int value)
    	{
    		Console.WriteLine("为int拓展的方法" + value);
    	}
    }
    
    //使用
    class Program
    {
    	static void Main(string[] args){
    		int i = 10;
    		i.speakValue();//为int拓展的方法10
    	}	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    补充:如果拓展的方法与原有方法重名,则后续调用的方法仍然是原方法逻辑。

    • 6.运算符重载(operator)
      概念:让自定义类和结构体能够使用运算符
      特点
      – 1.一定是个公有的静态方法
      – 2.返回值写在operator前
      – 3.逻辑处理自定义
      实例
    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;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    补充:算术运算符,条件运算符都可以被重载,逻辑运算符中仅有 逻辑非 可重载。条件运算符必须成对出现,有 > 一定有 < 。不能用ref 或 out关键字


    11.OOP特性之继承

    特性

    • 1.单根性:子类只能有一个父类
    • 2.传递性:子类可以间接继承父类的父类

    重点内容

    • 1.基础语法:class 类名 : 父类名

    • 2.里氏替换原则
      – 概念:任何父类出现的地方,子类都可以代替(父类容器装载子类对象)
      – 作用:方便对对象进行存储和管理
      – 实例:

    //Player是GameObject的子类(父类容器装载子类对象)
    GameObject player = new Player();
    
    • 1
    • 2

    – is关键字:

    //判断player是不是Player类型
    if (player is Player){}
    
    • 1
    • 2

    – as关键字:

    Player p = player as GameObject;
    
    • 1

    – is和as的联合运用:

    if (player is Player)
    {
    	Player p = player as Player;   //返回null或者player对象
    }
    
    • 1
    • 2
    • 3
    • 4
    • 3.继承中的构造函数
      执行顺序:… —> 父类的父类 —> 父类 —>当前类
      **父类的无参构造函数很重要!!!**子类实例化时默认调用父类的无参构造,被其他构造函数顶掉会导致报错。
      通过base调用指定父类构造:
    class Son:Father
    {
    //调用父类的i参数构造函数
    	public Son(int i) : base(i)
    	{
    		//...
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 4.万物之父与装箱拆箱
      object:万物之父,可以用object容器装载一切类型的变量。
      装箱拆箱:
      装箱:用object存值类型数据
      拆箱:把object里面的值转换出来
      好处:不确定存储类型时可以使用,方便参数的传递与存储
      坏处:存在内存的迁移,增加了性能消耗。

    • 5.sealed关键字(密封类)
      概念:让一个类不能再次被继承(绝育)
      意义:加强面向对象程序设计的 规范性 结构性与安全性。

    12.OOP特性之多态

    概念:多种状态,让继承统一父类的子类们执行相同方法时有不同表现。
    目的:同一父类的对象执行相同行为(方法)有不同表现 。
    作用:让同一个对象有惟一行为的特征。
    重点内容

    • 1.vob(virtual override base):
      – 实例:
    class GameObject
    {
    	public string name;
    
    	public virtual void Atk()
    	{
    		
    	}
    }
    
    class Player : GameObject
    {
    	public override void Atk()
    	{
    		base.Atk();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 2.抽象类与抽象方法
      – 抽象类概念:被abstract修饰的类,不能被实例化,可以包含抽象方法。
      – 抽象方法:没有方法体的纯虚方法,继承后必须去实现的方法

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

    – 使用规范:
    — 1.类可以继承多个接口,相当于行为合并
    — 2.类继承接口后,必须实现接口中所有成员

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

    13.面向对象补充

    重点内容

    • 1.命名空间namespace
      – 基本语法:```namespace MyGame{}``
      – 理解:类比Java中的包管理

    • 2.类前面可以加哪些关键字
      – 1.public:公有的
      – 2.internal:只能在该程序集使用
      – 3.abstract:抽象类
      – 4.sealed:密封类
      – 5.partial:分部类

    • 3.命名空间总结:
      – 1.命名空间是个工具包,用来管理类
      – 2.不同命名空间张,可以有同名类
      – 3.不同命名空间中互相使用,需要using引用或者指明出处
      – 4.命名空间可以包裹命名空间

    • 4.object类详解
      – 1.bool Equals:判断两个对象是否相等,值类型比较是否相等,引用类型比较两个引用是否指向一个内存地址。
      – 2.bool ReferenceEquals:专门比较引用类型的数据,传入值类型会始终返回false。
      – 3.Object MemberwiseClone:获取浅拷贝对象,新对象引用变量和老对象一致。

    • 5.string类与StringBuilder类
      对比:SB相比string来说引用了容量的概念,减少了扩容的操作,使对一个字符串进行修改操作时性能提高了。当容量满溢时,SB会自动扩容(16->32)。

    • 6.结构体和类的区别
      – 1.二者最大的区别体现在存储空间上,结构体是值类型,存储在栈上,而类是引用类型,存储在堆上。结构体具有OOP中的封装特性,但不具备继承和多态,因此大大减少了其使用频率。
      – 2.一些细节区别:总结

    1.结构体是值类型,类是引用类型
    2.结构体存储在栈中,而类存储在堆中
    3.结构体成员不能使用 protected关键字,而类可以
    4.结构体成员变量不能声明初始值,而类可以
    5.结构体不能申明午餐构造函数,类可以
    6.结构体申名有参函数构造后,无参构造不会被顶替
    7.结构体不能什么析构函数,类可以
    8.结构体不能被继承,类可以
    9.结构体需要再构造函数中初始化所有成员变量,类随意
    10.结构体不能被static修饰,类可以
    11.结构体不能再自己内部声明和自己一样的结构体变量,类可以

    – 3.结构体特别之处
    结构体可以继承接口,接口是行为的抽象

    • 7.抽象类和接口的区别
      – 1.相同点:

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

    – 2.区别:

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

    – 3.如何选择使用

    1.表示对象的用抽象类,表示行为拓展的用接口
    2.不同对象拥有的共同方法,可以使用接口来实现
    3.动物是一类对象,选择抽象类,而飞翔是一个行为,选择使用接口

    • 8.OOP七大原则
      目的:高内聚低耦合
      – 1.单一职责原则(SRP:Single Responsibility Principle):一个类只处理自己应该处理的内容
      – 2.开闭原则(OCP:Open-Closed Principle):对拓展开发,对修改关闭。
      – 3.里氏替换原则(LSP:Liskov Substitution Principle):任何父类出现的地方,子类都可以代替
      – 4.依赖倒转原则(DIP:Dependence Inversion Principle):要依赖于抽象,不要依赖于具体的实现
      – 5.迪米特原则(Law of Demeter):一个对象尽可能对其他对象少的了解,降低耦合度
      – 6.接口分离原则(ISP:Interface Segregation Principle):一个接口不需要提供太多行为,不要把所有行为都封装到一个接口
      – 7.合成复用原则(CRP:Composite Reuse Principle):尽量使用对象组合,而不是继承来达到复用的目的。(遵循迪米特原则)

    C#进阶语法

    1.ArrayList

    本质:本质是 一个 Object 类的数组。
    语法ArrayListList a = new ArrayList();
    查询工具类:https://learn.microsoft.com/zh-cn/
    补充ArrayList本质是一个Object数组,故在存储值类型数据时存在大量装箱拆箱操作,尽量少用该容器,之后会学习更好的数据容器。

    2.Stack和 Queue

    本质:本质也是 Object 类的数组,Stack是遵循 先进后出 的存储规则,而Queue遵循 先进先出 的存储规则。
    语法
    Stack stack = new Stack();
    Queue queue = new Queue();

    API不在此列举,养成自主查询文档的习惯!!!
    补充:同样涉及到许多装箱拆箱操作,会降低效率!

    3.Hashtable(散列表)

    本质:基于哈希代码组织起来的 键值对存储。提高查询效率。
    语法Hashtable hashtable = new Hashtable();
    重点

    • 1.不能出现重复键
    • 2.查找的时候直接使用数组方式:hashtable["111"]
    • 3.遍历方式:
    //遍历所有键值
    //方案1
    foreach(object item in hashtable.Keys)
    {
    	item;hashtable[item]
    }
    //方案2
    foreach(object item in hashtable.Values){}
    //方案3
    foreach(DictionaryEntry item in hashtable)
    {
    	item.Key,item.Value;
    }
    //迭代器遍历
    IDictionaryEnumerator myEnumerator = hashtable.GetEnumerator();
    bool flag = myEnumerator.MoveNext();
    while(flag)
    {
    	myEnumerator.Key;
    	myEnumerator.Value;
    	flag = myEnumerator.MoveNext();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4.泛型

    概念:实现了类型参数化,达到代码复用的目的
    泛型类和接口
    泛型方法

    public T Test<T>()
    {
    	return default(T);
    }
    
    • 1
    • 2
    • 3
    • 4

    作用

    • 1.不同类型对象的相同逻辑处理就可以选择泛型
    • 2.使用泛型可以一定情况下避免装箱拆箱
      总结
    • 1.申明泛型时,它是一个类型的占位符
    • 2.泛型真正起作用是在使用它的时候
    • 3.泛型占位字母可以又n个逗号隔开
    • 4.泛型占位字母一般是大写
    • 5.不确定泛型类型时,获取默认值 可以使用default(T)

    5.泛型约束

    作用:让泛型的类型有一定限制
    基本用法

    • 1.值类型:where 泛型字母 : struct
    • 2.引用类型:where 泛型字母 : class
    • 3.存在无参公共构造函数:where 泛型字母 : new()
    • 4.某个类本身或者其派生类:where 泛型字母 : 类名
    • 5.某个接口的派生类型:where 泛型字母 : 接口名
    • 6.另一个泛型类型本身或派生类:where 泛型字母 : 另一个泛型字母

    多个泛型约束

    class Test8<T,K> where T : class,new() where K:struct{}
    
    • 1

    6.List

    本质:可变类型的泛型数组
    语法List list = new List();
    增删改查API自查文档!!!
    补充:查的操作同样可使用数组类似的下标法取出。

    7.Dictionary

    本质:拥有泛型的 Hashtable
    声明Dictionary dictionary = new Dictionary();
    补充

    • 1.不能出现相同键。
    • 2.如果查询查不到对应值,则会直接报错(和Hashtable的区别之一)
    • 3.键值对一起遍历的代码样例:
    foreach(KeyValuePair<int,string> item in dictionary)
    {
    	item.Key,item.Value;
    }
    
    • 1
    • 2
    • 3
    • 4

    8.委托

    定义:方法的容器。可以理解为表示函数的变量类型。用来存储,传递方法。
    本质:本质上是一个类,用来定义方法的类型,不同函数必须对应各自格式(参数与返回值)一致的委托。

    语法访问修饰符 delegate 返回值 委托名(参数列表);
    重点内容

    • 1.一般写在namespace或者class语块中
    • 2.委托变量可以存储多个函数
    • 3.委托的使用方式:
    //定义一个int参数的无返回值的委托
    delegate void MyFun(int k);
    
    class Solution
    {
    	MyFun myfun;
    
    	public void haha(int param){...}
    	public void hehe(int param){...}
    
    	static void Main(string[] args)
    	{
    		//添加委托
    		myfun += haha;
    		myfun += hehe;
    		//调用
    		myfun(1);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 4.系统自带委托
      Action:无参无返回值函数委托
    Action action = Fun;
    action += Fun2;
    action();
    
    • 1
    • 2
    • 3

    Func:泛型返回值无参数委托

    Func<string> funcString = Fun4;
    Func<int> funcInt = Func5;
    
    • 1
    • 2

    Action<>:可以传n个参数无返回值函数委托

    Action<int,string,bool,K,V> action2 = Fun6;
    
    • 1

    Func<>:可以传n个参数有返回值的函数委托

    //最后一个参数为返回值,带out关键字
    Func<int ,string,bool,K,V,int> action3 = Fun7;
    
    • 1
    • 2

    9.事件

    概念:基于委托,是委托的安全包裹,让委托更具有安全性。是一种特殊的变量类型。
    语法访问修饰符 event 委托类型 事件名
    使用

    • 1.作为成员变量存在于类中
    • 2.委托怎么用,事件就怎么用
    • 3.与委托的区别:
      – 1.不能在类的外部赋值
      – 2.不能在类的外部调用
      – 3.事件不能被作为临时变量使用,委托可以。
      – 4.事件只可以使用+,-=进行添加或移除函数,委托任意。
    • 4.它只能作为成员存在于类,接口以及结构体中。

    10.匿名函数

    概念:没有名字的函数,主要配合委托和事件使用
    语法delegate (参数列表)
    样例

    //声明匿名函数
    Action a = delegate ()
    {
    	
    }
    
    a();
    a.Invoke();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    缺点:添加到委托或事件容器中不记录,无法单独移除。

    11.Lambda表达式

    概念:匿名函数的简写,与委托或者事件配合使用。
    语法(参数列表)=>{}
    使用

    • 1.无参无返回值
    Action a = () => {...}
    a();
    
    //甚至参数类型可以省略,与委托容器一致
    Action<int> a2 = (value) => {...}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    缺点:和匿名函数一样。
    补充

    • 1.内层函数可以引用包含在它外层的函数的变量,即使外层的函数执行已经终止。
    • 2.该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。

    12.List排序

    匿名函数排序案例

    //声明一个List
    List<Item> itemList = new List<Item>();
    itemList.Add(...);
    
    itemList.Sort(delegate (Item a ,Item b) => {
    	if (a.id > b.id)
    	{
    		return 
    	}
    });
    
    //lambda表达式写法
    itemList.Sort(( a , b )=>{
    	//升序
    	return a.id > b.id ? 1 : -1; 
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    13.协变逆变

    协变:和谐的变化,自然的变化,里氏替换原则,子类转父类。父类泛型委托装子类泛型委托
    逆变:逆常规变化,父类转子类。子类泛型委托装父类泛型委托
    作用

    • 1 仅能用于泛型接口和泛型委托中
    • 2.用out修饰的泛型,只能作为返回值
    • 3.用in修饰的泛型,只能作为参数
    • 4.遵循里氏替换原则的 用out和in修饰的 泛型委托 可以相互装载(有父子逻辑)。

    14.多线程

    进程:操作系统下 可以进行许多进程(前台,后台)。进程之间相互独立运行,互不干扰。也可以相互访问,操作…
    线程:操作系统能够运算调度的最小单位。其被包含在进程之中,是进程的实际运作单位。一个进程可以并发多个线程。

    语法:所需类:using System.Threading;
    声明:Thread t = new Thread(无参无返回值委托函数);
    开启线程:t.Start();
    设置后台线程:t.IsBackground = true;
    中止线程:t.Abort(); t = null
    线程休眠:Thread.Sleep(1000);//线程休眠1s

    锁机制lock

    • 1.语法lock(引用变量){...}
    • 2.用处:处理一些 寻路,网络通信等复杂计算 算法。

    15.俄罗斯方块实践

    场景模块核心类图
    在这里插入图片描述

  • 相关阅读:
    7. 核心功能(Core Features)
    逆向通达信 x 逆向微信 x 逆向Qt
    ES(Elasticsearch)中文检索使用笔记(一)
    Azure Machine Learning - 什么是 Azure AI 搜索?
    5.RabbitMQ高级特性
    Python Pandas数据处理作图——霍尔效应
    Vue移动 HTML 元素到指定位置 teleport 标签
    Vue3 + Tsx 集成 ace-editor编辑器
    IP网络矿用打点紧急广播方案
    02Python基础知识
  • 原文地址:https://blog.csdn.net/qq_55071342/article/details/127025076