目录
引用类型“改变后面被赋值的变量,也会改变原来的变量”数组默认为引用类型... 16
前言
首先感谢IT萌叔Jack的C#教学视频
我学习了大概一两周,写的这些笔记开始是在word上记录的,这篇大概1万字的笔记都是我学习IT萌叔Jack之后的总结,集合部分是看的up主唐老狮的视频,这篇笔记从数据类型到面向对象再到线程,事件,泛型。。。。等等都有写,但是还有反射和mysql没写完,后期用到的时候会补上的也有很多不足的地方希望大家在评论区打出来,我后期会更改。
输入输出
//输出语句:换行输出
Console.WriteLine("hool,word");
Console.WriteLine("hool c#");
//输出语句,无换行输出
Console.Write("hool word");
Console.Write("hool c#");
//输入语句:读取一行数据
string name = Console.ReadLine();
Console.WriteLine(name);
//输入语句:获取一个字符
ConsoleKeyInfo info = Console.ReadKey();
Console.WriteLine(info.KeyChar);
//不使用换行输出hell world
Console.Write("hello \n word");
Console.ReadKey();//输入任意键结束
字符串转换为其他变量
1.使用int.Parse()方法将字符串转换为整数类型,例如:int num = int.Parse("123");
2.使用int.TryParse()方法进行安全的转换,如果转换失败会返回false,例如:
使用to string方法将其他类型变为字符串类型
函数
1静态函数 使用static关键字的函数
- 静态函数可以直接通过类名调用,不需要创建类的实例。例如:ClassName.StaticFunction()。
- 静态函数只能访问静态成员,不能访问非静态成员。这是因为静态函数在类加载时就已经存在,而非静态成员需要通过类的实例才能访问。
- 静态函数不能使用this关键字,因为this表示当前对象实例,而静态函数没有对象实例。
- 静态函数可以访问静态变量和静态方法,静态变量和静态方法在类加载时就已经存在。
- 静态函数可以被继承和重写。子类可以通过override关键字重写父类的静态函数,但是不能通过base关键字调用父类的静态函数。
- 静态函数可以在非静态函数中调用,但是非静态函数不能直接调用静态函数,需要通过类名调用。
- 静态函数常用于工具类、辅助函数等不需要依赖对象实例的场景。
2非静态函数 没有static关键字的函数
3语法:
访问修饰符 【静态修饰符】 返回值类型 函数名(参数列表)
{
函数体
【return返回值】;#返回函数要和返回值类型相同
}
Public static void【返回类型】 name(string[]【类型】 args【名字】)
{
Return(返回值)
}
Void 无返回值
函数名 首字母大写
修饰符
1.访问修饰符(Access Modifiers):这些修饰符控制类成员的可见性和访问级别。
public:成员可以在任何地方访问,没有访问限制。
private:仅在其声明的类中可以访问成员。
protected:允许在声明它的类以及该类的派生类中访问成员。
internal:成员只能在同一程序集的其他类和代码中访问。
static修饰符:使成员成为类级别的,而不是实例级别的。static成员属于类,而不是类的任何特定实例,因此可以在不创建类实例的情况下访问它们。
const修饰符:使变量成为编译时常量,其值在编译时确定且无法更改。
readonly修饰符:使字段在其声明或构造函数中仅可赋值一次,然后保持不变。
virtual修饰符:允许在派生类中重写方法或属性。
override修饰符:在派生类中用于重写基类中的virtual或abstract成员。
静态函数
1静态函数 使用static关键字的函数
静态函数属于类本身,而不是类的实例。因此,它们可以直接通过类名调用,而不需要创建类的实例。例如:ClassName.StaticMethod();
静态函数不能访问类的非静态成员(如实例变量或实例方法),因为它们不依赖于类的实例。静态函数只能访问静态成员。
2非静态函数 没有static关键字的函数
非静态函数属于类的实例,需要创建类的实例来调用。例如:
public string Name { get; set; }的含义是:定义一个名为Name的公共属性,其数据类型为string。这个属性可以在类的实例中读取(通过get访问器)和写入(通过set访问器)
非静态函数可以访问类的实例成员(如实例变量)和静态成员
3语法:
访问修饰符 【静态修饰符】 返回值类型 函数名(参数列表)
{
函数体
【return返回值】;#返回函数要和返回值类型相同
}
Public static void name(string[]【类型】 args【名字】)
{
return
}
Void 无返回值
函数名 首字母大写
数据类型
b:bit比特(最小单位)位
B : = 8b, Byte(字节)简称B 0000 0000
1024B=1kB ,1024kB=1MB , 1024MB=1tB
1. 有符号整数类型:
- sbyte(有符号的8位整数):占用1个字节,范围为-128到127。
- short(有符号的16位整数):占用2个字节,范围为-32,768到32,767。
- int(有符号的32位整数):占用4个字节,范围为-2,147,483,648到2,147,483,647。
- long(有符号的64位整数):占用8个字节,范围为-9,223,372,036,854,775,808到9,223,372,036,854,775,807。
2. 无符号整数类型:
- byte(无符号的8位整数):占用1个字节,范围为0到255。
- ushort(无符号的16位整数):占用2个字节,范围为0到65,535。
- uint(无符号的32位整数):占用4个字节,范围为0到4,294,967,295。
- ulong(无符号的64位整数):占用8个字节,范围为0到18,446,744,073,709,551,615。
3. 浮点型数据类型:
- float(单精度浮点数):占用4个字节,精度约为6~7位有效数字。
- double(双精度浮点数):占用8个字节,精度约为15~16位有效数字。
4. 字符型数据类型:
- char(16位Unicode字符):占用2个字节,用于表示一个单一的 Unicode 字符,可以看做无符号的 16 位整数。
-String
5. 布尔型数据类型:
- bool(真或假的布尔值):占用1个字节,只有两个取值true和false。
标识符
- 使用全部字母大写的方式,并用下划线分隔单词。
- 示例:MY_CONSTANT、APP_VERSION。
请注意,以上只是一些常见的命名规则,具体的命名规范应根据实际情况和团队约定来确定。另外,还需注意不要使用C#保留关键字作为标识符。
处理异常程序1.4
try{可能出现错误的代码}
catch(Exception【异常类型】ex【变量名ex是Exception方法封装后的变量】){处理异常代码Console.WritLine(ex.Message)[用。.Message方法输出错误的原因]}
finally {始终要执行的代码}
字符串链接方法
String name = “名字1”;
Int age = 12;//年龄
//用+号链接
Console.WriteLine (“姓名:”+ name + “年龄:” + age)
//格式化输出
Console.WriteLine(string.Format (“姓名:{1} 龄:{2} “,name,age))
Console.WriteLine(string.Format ($“姓名:{ name } 龄:{ age } ”)
显示转换和隐藏转换
1显示转换: 显示转换是指将一个较大范围的数据类型转换为较小范围的数据类型。这种转换可能会导致数据丢失或溢出。要进行显示转换,可以使用强制类型转换操作符,即将目标类型放在括号中,并在其前面加上要转换的变量。例如:
2隐藏转换: 隐藏转换是指将一个较小范围的数据类型转换为较大范围的数据类型,或者将派生类转换为基类。这种转换是隐式完成的,不需要使用强制类型转换操作符。例如:
在上面的示例中,我们将一个int类型的变量num隐式转换为double类型的变量d。由于double类型的范围比int类型大,因此可以进行隐藏转换。
分支语句Swtich,if
If(条件){分支}else{分支}
switch(expression【变量】)
{ case value1://case为多个
//当expression等于value1时执行这里的代码
break;
case value2:
//当expression等于value2时执行这里的代码
break;
循环语句
for (初始化[eg :i = 0]; 条件判断【eg: i < n】; 迭代器【步长表达式】){// 循环体}
while (condition【条件表达式】){// 循环体}
do{循环体} while(条件表达式);
break和continue关键字
break停止循环 Return停止循环(返回值)
continue跳出当前循环跳到外面的循环
Eg:while(true){ break }
运算符和逻辑运算符
与或非:&& | ^
加减乘除:+ - * /
复合运算符 sum += 5 ##== sum = sum + 5
##重点自己加减:
--x 是前缀递减运算符,表示先将 x 的值减 1,然后再返回递减后的值。
x-- 是后缀递减运算符,表示先返回 x 的原始值,然后再将 x 的值减 1。
数组
特性: 1相同数据类型,
2固定大小,3有序的内存空间链接【下标从0开始】
数组声明:type[]名称;type数据类型
读取数组长度:Console. WriteLine (名称.Length);
通过下标访问(从0开始):Console.Writeline(名称【0】);
Array关键字可以控制数组排序找 下标
引用类型“改变后面被赋值的变量,也会改变原来的变量”数组默认为引用类型
定义引用变量需要使用相应的数据类型,并使用关键字ref或out进行声明。
使用ref关键字:
复制代码
int num = 10;
ref int refNum = ref num;
在这个例子中,refNum是一个引用变量,它被赋值为num的引用。这意味着对refNum的任何修改都会反映在num上。
使用out关键字:
复制代码
int result;
if (int.TryParse("123", out result))
{
Console.WriteLine($"解析成功:{result}");
}
else
{
Console.WriteLine("解析失败");
}
在这个例子中,result是一个引用变量,在调用TryParse()方法时,它作为参数传递给方法。TryParse()方法将结果通过out关键字返回到result中。
程序调试
查看局部变量
可以点击vs调试->单步/逐语句
里面还可以看局部变量
添加断点
用ref和out改改变函数的值
Ref和out不能有默认值和返回值
定义时间只能用void name(ref int X)x不能是x=多少多少
用ref将要传入的参数变成引用变量
Out与ref的区别就是out不用给value赋值
可变参数
本质是数组,定义方式是用params修饰
定义了一个可以传入任意长度的数组。
Out int sum 定义一个引用变量
调用时
用out时间不用给dd初值,
参数默认值
参数默认值指的是参数默认值是指在方法或函数定义时为参数提供的预设值。当调用该方法或函数时,如果没有提供相应的参数值,就会使用默认值。
函数重载
相同的名字,可以使用不同的参数,调用同一名字时会使用对应的能用的那个函数
函数递归
函数自己调用自己实现循环,但是要加入一个退出以免死循环。
枚举
1定义
被命名的整形常量集合,一般被用于表示有限的状态。
语法: enum 枚举名{
* 枚举值1
* 枚举值2
* 枚举值3}
如下图
调用时要先定义枚举类型的变量如下图
2枚举类型应用
枚举一般结合switch语法使用
3特点
1枚举底层是采用整形数据经行存储,从0开始
以上代码输出后是1因为是第二个
2.枚举类型和整数类型可以用显示转换相互转换
结构体
结构体的定义:结构体是开发者自定义类型的一种,可以将不同数据类型,但相关的多个数据作为一个整体进行描述数据结构,
struct Role//定义一个描述玩家
{
public string name;//名字
public int age;//年龄
public E_Gender xb;//性别用了枚举类型
public int atk;/
}
Struct 定义结构体 public
是一种访问修饰符,用于指定类的成员(如字段、属性、方法等)可以被类的外部代码访问。也就是说,
public
成员可以在该类的任何地方都可以被外部代码访问,包括其他类、命名空间等。
结构体内可以再定义函数
调用时可以直接用.Rolel的方法调用
总的来说,结构体是一种轻量级的数据类型,适用于描述简单的数据结构,例如点、矩形、颜色等。如果需要描述更复杂的数据结构,应该使用类。
源码解析
整理数据类型
基本类型:传递的是数据本身
Sbyte,short,int,long(1,2,4,8)
Byte,ushort,uint,ulong (1,2,4,8)
Bool,char,float,double(1,2,4,8) decimal(16)
自定义类型:enum(枚举:能穷举完的所有类型),struct(结构体:简单的合在一起)
引用类型:传递的是数据地址可以用关键字ref和out,数组默认应用类型
数据类型对应的结构体
编程语言
C:
C语言是一种底层的编程语言,它是一种通用的、面向过程的编程语言,广泛应用于系统编程、嵌入式开发和科学计算等领域。
Pascal:
Pascal语言是一种结构化的编程语言,它是面向过程的编程语言,主要用于教育和科学计算等领域。
BASIC:
BASIC语言是一种入门级的编程语言,它是面向过程的编程语言,主要用于教育和小型应用程序的开发。
C#:
C+是一种通用的、高效的面向对象编程语言,它是C语言的扩展,支持面向对象编程和泛型编程,广泛应用于系统编程、游戏开发和科学计算等领域。
C#:
C#是一种现代化的、面向对象的编程语言,它是微软公司开发的一种语言,主要应用于Windows桌面应用程序、web应用程序和游戏开发等领域。
Java:
Java是一种广泛应用的面向对象编程语言,它是一种跨平台的编程语言,可以运行在不同的操作系统和硬件平台上,具有丰富的类库和工具支持。
Python:
Python是一种简单易学、灵活多用途的面向对象编程语言,具有强大的类库和工具支持,广泛应用于Web开发、数据分析和人工智能等领域。
Ruby:
RUby是一种简洁、优雅的面向对象编程语言,它具有灵活的语法和强大的元编程能力,广泛应用于Web开发和脚本编程等领域。
面向过程的语言:(pop):有C,pascal,basic
函数由一系列函数或者过程(一组预定的指令序列,没有返回值)组成,这些函数或过程处理输入数据返回输出数据,整个程序的结构类似于一个流程图。在面向过程种,程序的设计和实现主要关注解决问题的过程和实现显示细节,而不太关注数据的抽象和封装
面向对象的语言(oop)有:C++,C#,Java,Python,Ruby…..
在面向对象编程中,程序主要由类和对象组成,每个对象包含数据和相关的方法,通过对象之问的交互实现程序的功能。在面向对象编程中,程序的设计和实现主要关注数强的抽象和封装,而不太关注解决问题的过程和实现细节。
总结面向对象1,找合适的对象找合适做合适的事情。2如果有定义好的类,对象直接使用,若没有就自定义,对象工作。
面向对象
#实战案例计算机
构造函数
创建 :访问修饰符 类名(参数列表){初始化字段的语句}
使用: 类名 name = new 类名(参数列表)
1构造器的名称必须与类名相同。
2构造器没有返回类型,不允许使用void或其他类型作为返回类型。但可以用函数返回类型
3.构造器可以有参数,也可以没有参数。没有参数的构造器称为默认构造器(Default Constructor)。
4.构造器可以被重载:
这意味着一个类可以有多个构造器,只要它们的参数列表不同。
5.创建类的实例时:
使用关键字new和相应的构造器来调用构造器。
6.系统会自动创建一个无参数的构造函数:
但是一旦开发者自定义了有参数的构造函数,那么系统就不会再创建无参数的构造函数。关键字调用父类的构造函数。
在名字后面加:this()因为是函数重载所以this是自己的意思,参数不一样来决定用那一函数。
7.构造函数用public修饰符,如果需要单例则使用private修饰符
8.this
this关键字表示当前对象的引用
类和对象
类就是一类事物的抽象,具体值具有相同特征的一类事物。比如人,鸟
对象就是类的具体事物,或者方法。
定义类:class 类名(帕斯卡命名){
特征(变量进行表示);//定义一个特征学号和名字:这里的每一个特征也别叫做字段。
public int id;
Public string name;
行为(函数经行表示)// 定义行为介绍自己
public void Print(){Console.WriteLine($”我的名字是{name},我的学号是{ id}”)
}
以上就定义完了一个类和对象
使用时在主要的类里面的main里面
来个 类名 name = new 类名
name.Print();//使用定义的Print方法就可以了
析构函数
主要用于在来机会少该对象之前进行调用,主要用于对象资源的释放
构造函数是创建对象的时候使用new关键字进行调用;
析构韩式是对象销毁的时候使用垃圾回收器进行调用
在C#中,析构函数是一种特殊的方法,用于在垃圾回收器销毁对象之前执行一些清理操作。析构函数的名称与类名相同,但前面有一个`~`字符。
在C#中,垃圾回收器是自动管理内存的,它会定期扫描所有不再引用的对象并释放它们占用的内存。垃圾回收器使用的是可达性分析算法,即它会判断哪些对象可以被访问到,哪些对象已经不再被访问,然后释放那些不再被访问的对象。
在垃圾回收器销毁一个对象之前,会自动调用该对象的析构函数。析构函数用于执行一些清理操作,例如关闭文件、释放资源等等。
以下是一个示例,其中`Person`类具有一个析构函数:
```
在这个示例中,我们定义了一个名为`Person`的类,并为其定义了一个析构函数。在析构函数中,我们简单地输出一条消息来指示对象已被销毁。
以下是使用这个类的一个示例:
在这个示例中,我们创建了两个`Person`对象,并它们分配给`person1`和`person2`变量。然后,我们将这两个变量设置为`null`,以便它们不再引用这些对象。最后,我们调用`GC.Collect()`方法来强制调用垃圾回收器。
在调用`GC.Collect()`方法之后,垃圾回收器会扫描所有不再引用的对象,并调用它们的析构函数。在这个示例中,我们将会看到两次输出消息,即`"对象已销毁"`,这表明这两个对象已经被垃圾回收器销毁。
实际上在正常情况下不需要手动调用GC.Collect()方法来启动垃圾回收器。示例中只是为了演示如何在代码中手动调用垃圾回收器。
在正常情况下,当不再使用对象时,垃圾回收器会自动释放内存,并在销毁对象之前自动调用析构函数。如果我们强制调用GC.Collect()方法,垃圾回收器会立即启动并释放内存,但这可能会影响应用程序性能。
因此,在正常情况下,我们不需要手动调用GC.Collect()方法来销毁对象和释放内存,因为垃圾回收器会自动管理内存。
属性封装
字段:当我们使用交量描述一类事物的特征时,这些变量称之为该类的宇段。字段的访问可以直接通过对象方式访问
属性:属性就是对字段的封装。主要提供对字段的设置和访问操作。
语法:public数据类型 属性名{ set{}get{变量 = value }} value是默认被传入的值
特点
Get和set 也是索引器
1.属性封装中get和set可以进行访问修饰的修饰符默认是public,不能显示指定
2.可以使用private【私有访问修饰符】进行修饰,但是不能get和set都用private。
3.get和set可以单独使用
实例
继承例子在最后
定义:继承是讲一个类的成员直接在另一类里面进行使用。
前提:继承的两个类之间要有内在的联系:
实现语法:类名class Rectangle:Shao //Rectangle是子类Shape是父类
特点:
1.C#中的继承只能单继承
2.子类只能使用父类中非私有修饰符修饰的字段,同 (非private,非静态)
里氏转换
定义:使用父类的引用变量存储子类的对象
特点:此时只访问父类成员
使用:如果需要访问子类成员需要类型转换 显示转换有
S是父类的参数名字 转换成子类 类型Rectangle
应用场景:主要用于参数列表的数据类型,将参数类型定义为父类,就可以传递任何子类的对象
扩展:object是多有类的父类。
子类初始化过程有单独的例子
会调用基类的构造函数来完成基类的初始化。然后,子类的构造函数会被调用来完成子类自身的初始化。
定义:子类初始化过程指的是子类对象到初始化类对象的经过
阶段:1,创建初始化子类对象字段阶段:创建子类对象会默认调用父类的无参数构造函数先创建父类对象。
2,创建子类对象阶段:先初始化父类的字段再初始化子类的字段:
3,当我们继承有关系的时候,一定要给父类提供无参数的构造函数,如果子类的构造函数没有通过base关键字显式调用父类的构造函数,那么子类的构造函数会默认尝试调用父类的无参数构造函数。如果父类没有提供无参数构造函数,那么这会导致编译错误。
关键字:[:base(name) base.调用父类的构造函数并传递name] 【:this(name)调用自己参数一个的构造函数并传递name】
多态
多态的定义是在同名函数里面 直接用父类存储子类,于是一个父类有多个子类。一个变量有多个状态。
给父类增加子类的变量,调用默认是调用父类,直接调用子类的方法用
父类与子类方法同名
语法父类用virtual修饰
子类用override修饰 override来显式地表示一个方法是对父类或接口中方法的重写。
那么用父类存储子类调用时默认是子类,如果不用修饰符那么默认就是父类
想要在子类与父类的同名
抽象类
定义:如果定义一个类的时候描述该功能不知道如何描述,此时可以先将函数定义为抽象函数,再将该类定义为抽象类
实现:要用abstract函数修饰函数和类。
特点: 1,有抽象函数一定是抽象类
* 2,抽象函数不能有函数载体{}
* 3,如果继承一个抽象类,那么必须实现抽象类中的抽象函数或者将该类声明为抽象类
使用时一定要注意:1.抽象函数修饰符为protrcted或public,子类实现抽象函数之后访问修饰符要和抽线函数修饰符相同。
2.抽象类中不一定有抽象函数。
作用:抽象类的主要作用是让别的子类来继承,这样就可以实现我们类的设计结构的具有拓展性。Test()作为父类存储子类的所有对象。
```C#
Public abstract class Test(){}
接口
定义:如果有多个类之间有内在联系,那么可以将相同的特征和行为定义到类里面去,这样子类就可以继承下来。
但是有了多个不相关的类有共同的行为,那么此时该行为就可以定义在接口中。
举例:
1、有内在关系的类:动物类(姓名、体重、吃饭、睡觉)、人类、员工类
2、没有关系的类:接口(飞)、鸟类、飞机类
语法:
3,开头写大写的I
interface 接口名{
1/ 只能出现属性、函数、索引器、事件
1/ 不能出现字段、构造函数
11 所有的成员访问修饰符默认都是public的,可以不写
1/ 函数没有函数体
}
作用:
1、定义接口的目的是为了让实现类进行实现该接口的功能
特点:
1,接口可以相互继承
2,接口可以多继承
密封函数
定义:sealed主要用于修饰类和函数,修饰的类称之为密封类不能被继承,修饰的函数不能被重构
修饰函数不能被继承
修饰类类不能用abstract和override
命名空间
命名空间是类的一个管理工具,防止类名的冲突
举例:生活 北京市,朝阳区,李四 太南京市,高星区,李四
语法:namespace 名称{//类,结构体,枚举...}
默认按照项目名称空间为命名空间的名字 吧
使用:(unsing 命名空间的名称)\\(然后在要使用的地方用命名空间的名称).函数名称
Using 命名空间的名字
Closs name
{static void mian(string[] args){ 命名空间的名字 = new();}}//实例化
或者用命名空间的名字.方法。
万物之父方法
GameObject中的静态方法
- GameObject.Instantiate(gameObject, position, rotation): 复制一个现有的GameObject实例并设置其位置和旋转。Quaternion.identity; // 不旋转
- GameObject.Find(name): 返回场景中名为指定名称的GameObject。
- GameObject.FindWithTag(tag): 返回具有指定标签的第一个GameObject。
- GameObject.FindGameObjectsWithTag(tag): 返回具有指定标签的所有GameObject的数组。
- GameObject.FindObjectsOfType(type): 返回场景中所有指定类型的GameObject的数组。
- GameObject.FindObjectOfTypeInRange(type, range): 在指定范围内查找具有指定类型的第一个GameObject。
- GameObject.DontDestroyOnLoad(gameObject): 使指定的GameObject在加载新场景时不被销毁。
- GameObject.Destroy(gameObject): 立即销毁指定的GameObject。
- GameObject.DestroyImmediate(gameObject): 立即销毁指定的GameObject并断开其所有组件。
- GameObject.SetActive(bool): 启用或禁用指定的GameObject及其所有子对象。
Object成员方法MemberwiseClone
Object
在C#中,object 类是所有类型的基类。它定义了一些基本的方法,这些方法可以被所有的对象继承和使用。以下是 object 类中定义的一些重要方法:
- Equals(object obj): 用于确定当前对象是否等于指定的对象。这个方法通常被重写以提供类型特定的相等性比较。
- GetHashCode(): 用于获取对象的哈希码。这个方法通常被重写,以便与 Equals 方法一起工作,确保相等的对象具有相同的哈希码。
- ToString(): 返回一个表示当前对象的字符串。这个方法通常被重写以提供更有意义的对象描述。默认打印会执行以下这个
public string ToString()
{
return base.ToString();
}
如果你没有在一个类中重写ToString()
方法,那么当你调用该对象的ToString()
方法时,它会返回该对象的类型名以及该对象在内存中的哈希码的无符号十六进制表示。
- GetType(): 返回一个表示当前对象运行时类型的 Type 对象。
- MemberwiseClone(): 创建当前对象的一个浅拷贝。这个方法通常被重写以实现深拷贝。
- Finalize(): 在垃圾回收器准备回收对象之前,允许对象尝试释放资源并执行其他清理操作。在C#中,你通常使用析构函数(destructors)而不是直接重写 Finalize 方法。
- ReferenceEquals(object objA, object objB): 这是一个静态方法,用于确定两个对象实例是否引用同一个对象。
public class Enemy
{
private int id;
private string name;
public int Id { get => id; set => id = value; }
public string Name { get => name; set => name = value; }
public Enemy(int id, string name)
{
this.Id = id;
this.Name = name;
}
public Enemy()
{
}
//重写TOString方法
public override string ToString()
{
return $"Id = {this.id} ,name = {this.name}";
}
//重写Equals
// override object.Equals
public override bool Equals(object obj)
{
Console.WriteLine($"重写的Equals");
Enemy enemy = obj as Enemy;
bool tag = false;
if (this.id == enemy.id && this.id == enemy.id)
{
tag = true;
return tag;
}
return tag;
}
//当变量相等时他的HashCode值也相等
public override int GetHashCode()
{
return this.id.GetHashCode()+this.name.GetHashCode();
}
}
public class NewClass
{
public Object obj;
public void z1()
{
obj = new object();
//和ToString一样
Console.WriteLine($"obj:{obj}");
//返回对象的字符串表示形式,通常是对象的类型名。
Console.WriteLine($"obj.ToString:{obj.ToString}");
//返回对象的运行时类型。
Console.WriteLine($"obj.GetType:{obj.GetType}");
//返回一个布尔值,表示对象与其自身是否相等(在这种情况下,结果总是true)
Console.WriteLine($"obj.Equals:{obj.Equals}");
//返回对象的哈希码,一个整数,通常用于散列数据结构如哈希表。
Console.WriteLine($"obj.GetHashCode:{obj.GetHashCode}");
//静态函数
//它用于比较两个引用类型(引用类型是类的实例或数组)是否引用同一个对象
//返回false
Console.WriteLine($"{object.ReferenceEquals(new object(), new object())}");
//返回True
object obj2 = obj;
Console.WriteLine($"{object.ReferenceEquals(obj2, obj)}");
//自定义类是object的子类:自定义结果Objects.Enemy:返回项目空间下的类
Enemy enemy = new Enemy(1234,"dddd");
Enemy enemy_2 = new Enemy(1234,"dddd");
Console.WriteLine($"自定义结果{enemy}");//默认执行ToString方法
Console.WriteLine($"自定义结果2{enemy.ToString}");//在前面重写了
//默认比较地址是否相同,重写比较id和name
Console.WriteLine($"用enemy比较enemy2的结果{enemy.Equals(enemy_2)})");
}
}
String
#region //创建对象
string str1 = "hell C#";
string str2 = "hell" + "C#";
string str3 = new string(new char[] { 'h', 'e', 'l', 'l,', ' ', 'C', '#' });
Console.WriteLine($"{str1},{str2},{str3}");
#endregion
#region //比较
Console.WriteLine($"{str1}=={str2}");
Console.WriteLine($"{object.ReferenceEquals(str1, str2)}");
#endregion
#region 属性
Console.WriteLine($"{str1.Length},{str2.Length},{str3.Length}");
#endregion
#region 静态函数
//格式化
Console.WriteLine(string.Format("{0}{1}","hell","C#"));
//合并
Console.WriteLine(string.Concat(new object[]{1,true,12.3,"ddd"}) );
//拼接
Console.WriteLine(string.Join("_",new object[]{1,,true,12.3}));
#endregion
#region 非静态函数
//查找返回索引(第一个的),找不到返回-1
Console.WriteLine(str1.IndexOf("l"));
//查找返回索引(到数第一个的)
Console.WriteLine(str1.LastIndexOf("l"));
StringBuilding
StringBuilder是C#中用于处理字符串的一个非常有用的类。它提供了高效的方式来构建和修改字符串,特别是当字符串很大或者需要频繁修改时。
主要特点:
- 性能优势:与String不同,StringBuilder是可变的,这意味着你可以在不创建新对象的情况下修改它。这对于大量拼接或替换操作来说非常高效。
- 线程安全:StringBuilder是线程安全的,这意味着在多线程环境中,多个线程可以同时修改StringBuilder对象,而不会产生冲突或数据不一致的问题。
- 可变长度:与固定长度的String不同,StringBuilder的长度是可变的,可以根据需要增长或缩小。
基本使用方法:
- 构造:可以使用默认构造函数创建一个空的StringBuilder对象,或者传递一个初始字符串来初始化它。
csharp复制代码
StringBuilder sb = new StringBuilder(); | |
StringBuilder sbWithInitialString = new StringBuilder("Hello"); |
- 追加文本:使用Append方法可以添加文本到StringBuilder的末尾。
csharp复制代码
sb.Append(" World"); // 结果为 "Hello World" |
- 插入文本:使用Insert方法可以在指定位置插入文本。
csharp复制代码
sb.Insert(6, "beautiful "); // 结果为 "Hello beautiful World" |
- 删除和替换:使用Remove和Replace方法可以删除或替换文本。
csharp复制代码
sb.Remove(0, 5); // 删除前5个字符,结果为 "World" | |
sb.Replace("World", "C#"); // 将 "World" 替换为 "C#",结果为 "C#" |
- 获取最终字符串:使用ToString方法可以将最终的StringBuilder内容转换为字符串。
csharp复制代码
string finalString = sb.ToString(); // 将StringBuilder的内容转换为字符串并赋值给finalString变量,此时finalString的值为 "C#" |
注意事项:虽然StringBuilder在处理大量字符串时提供了性能优势,但如果只是简单地拼接少量字符串,使用String连接操作符(+)可能更加简洁和高效,因为它们会被编译器优化。只有当需要大量拼接或修改字符串时,才应该考虑使用StringBuilder。
StringBuilder sbt1 = new StringBuilder();
StringBuilder sbt2 = new StringBuilder("帅哥");
Console.WriteLine($"{sbt1}");
Console.WriteLine($"{sbt2}");
//同sbt2直接输出
Console.WriteLine($"{sbt2.ToString()}");
//容量一般为16多余的就会*2扩容以下会显示32
sbt2.Append("12345678");
sbt2.Append("12345678");
sbt2.Append("1");
Console.WriteLine(sbt2.Capacity);//Capacity显示长度
#region 操作
StringBuilder sb3 = new();
sb3.Append("English_");
sb3.Append("Chinese_");
sb3.AppendFormat("{0}_{1}","asd_","dasf_");
Console.WriteLine($"{sb3}");
//插入
sb3.Insert(5,"_ing_");
Console.WriteLine($"{sb3}");
//删除
sb3.Remove(5,5);
Console.WriteLine($"{sb3}");
//查找
Console.WriteLine($"{sb3[1]}");
//替换
sb3.Replace("E","e");
Console.WriteLine($"{sb3}");
//长度
Console.WriteLine($"{sb3.Length}");
Math
Math.Round: 对一个数进行四舍五入。
Math.Ceiling: 向上取整。
Math.Floor: 向下取整。
Math.Pow: 计算一个数的幂。
Math.Sqrt: 计算一个数的平方根。
Math.Abs: 计算一个数的绝对值。
Math.Max 和 Math.Min: 返回两个数中的最大值和最小值。
Math.PI 圆周率
Math.E 自然数
集合
system.collections
ArrayList、Stack、Queue、HashTable
特点:
1、可以存储任何类型的数据
2、取出数据处理的时候非常繁琐:类型判断,类型转化,逻辑处理
解决?集合本质是存储任意类型的数据
解决问题的方案就是限制集合容器只能存储具体的一个数据类型。(泛型集合)
*/
#region 泛型集合
/*
system.Collections.Generic
List<T>,Stack<T>,Queue<T>,Dictionary<k,vk
List<int>
*/
ArrayList(相当于列表)
Enumerlor指向堆最上面的元素
Curent指向当前下一个的元素
enumerator.MoveNext()//enumerator向下移动
//创建空容器
ArrayList arraylist = new();
ArrayList list = new();0
//增加
arraylist.Add("北京");
arraylist.Add("上海");
list.Add("地名:");
list.AddRange(arraylist);
Print(list);
//插入
list.Insert(1, "重庆");
Print(list);
//查
Console.WriteLine($"长度:{list.Count}");
Console.WriteLine($"容量:{list.Capacity}");
Console.WriteLine($"元素:{list[2]}");
//改
list[1] = "\"改\"";
Print(list);
//删
list.Remove("\"改\"");//删值
Print(list);
list.RemoveAt(1);//索引
#region 清空
//list.Clear();
#endregion
#region 遍历
foreach (var item in list)//相比于for而言不需要知道list的长度
{
Console.Write(item + " ");
}
Console.WriteLine("foreach的遍历");
#endregion
#region 迭代器
IEnumerator enumerator = list.GetEnumerator();// Enumerlor指向堆最上面的元素
while (enumerator.MoveNext())// enumerator.MoveNext()//enumerator向下移动
{
Console.Write($"{enumerator.Current} ");// Curent指向当前下一个的元素
}
Console.WriteLine("迭代器");
#endregion
}
public void Print(ArrayList arraylist)
{
for (int i = 0; i < arraylist.Count; i++)
{
Console.Write($"{arraylist[i]} ");
}
Console.WriteLine($"");
}
Queue
1,先进先出
2,可以存储重复元素
/*定义:queue是C#的一个容器类,简称队列,底层用object[]实现,封装了CRUD方法。
*特点:
1,先进先出
2,可以存储重复元素
*/
public void z1(){
//创建容器
Queue queue = new Queue();
//进入队列
queue.Enqueue("jack");
queue.Enqueue("lucy");
queue.Enqueue("mark");
//查看数量
Console.WriteLine($"queue队列的容量:{queue.Count}");
Console.WriteLine($"{queue.Peek()}");//看第一个
//出队列
queue.Dequeue();
Console.WriteLine($"{queue.Peek()}");
Console.WriteLine($"queue队列的容量:{queue.Count}");
//判断
Console.WriteLine($"{queue.Contains("mark")}");
//循环出列
int Count = queue.Count;
for(int i = 0; i < Count ; i++){
object obj = queue.Dequeue();
Console.WriteLine($"{obj}");
}
//清空
queue.Clear();
Stact
//创建Stack对象
Stack stack = new Stack();
//增
stack.Push("子弹01");
stack.Push("子弹02");
stack.Push("子弹03");
stack.Push("子弹04");
stack.Push("子弹05");
//查:输出全部的个数
Console.WriteLine($"{stack.Count}");
//查看栈顶元素
Console.WriteLine($"{stack.Peek()}");
//删
stack.Pop();
Console.WriteLine($"{stack.Peek()}");
//遍历 从栈顶开始遍历
object[] obj = stack.ToArray();
foreach(var i in obj){
Console.Write($"{i} ");
}
Console.WriteLine($"");
int Count = stack.Count;
//弹出子弹
for(int i = 0; i<Count; i++){
Console.WriteLine(stack.Pop());
}
Console.WriteLine($"{stack.Count}");
//清空
stack.Clear();
Hashtable
- 引入命名空间
要使用 Hashtable,首先需要引入 System.Collections 命名空间:
csharp复制代码
using System.Collections; |
- 创建 Hashtable
创建一个新的 Hashtable 实例:
csharp复制代码
Hashtable hashtable = new Hashtable(); |
- 添加元素
使用 Add 方法向 Hashtable 中添加元素:
csharp复制代码
hashtable.Add("key1", "value1"); | |
hashtable.Add("key2", "value2"); | |
hashtable.Add("key3", 123); |
注意:键是唯一的,如果尝试添加具有相同键的项,则会抛出异常。
4. 访问元素
通过键访问 Hashtable 中的元素:
csharp复制代码
object value = hashtable["key1"]; | |
Console.WriteLine(value); // 输出: value1 |
由于 Hashtable 是非泛型的,因此当你从 Hashtable 中检索值时,你将得到一个 object 类型的值。你可能需要进行类型转换以获得期望的类型。
5. 更新元素
通过键更新 Hashtable 中的元素:
csharp复制代码
hashtable["key1"] = "newvalue1"; |
- 检查键是否存在
使用 Contains 方法检查 Hashtable 中是否存在特定的键:
csharp复制代码
bool hasKey = hashtable.ContainsKey("key1"); | |
Console.WriteLine(hasKey); // 输出: True |
- 删除元素
使用 Remove 方法从 Hashtable 中删除元素:
csharp复制代码
hashtable.Remove("key1"); |
- 遍历 Hashtable
使用 foreach 循环遍历 Hashtable 中的所有键值对:
csharp复制代码
foreach (DictionaryEntry de in hashtable) | |
{ | |
Console.WriteLine($"Key: {de.Key}, Value: {de.Value}"); | |
} |
- 获取 Hashtable 的大小
使用 Count 属性获取 Hashtable 中的元素数量:
csharp复制代码
int count = hashtable.Count; | |
Console.WriteLine(count); |
- 清空 Hashtable
使用 Clear 方法清空 Hashtable 中的所有元素:
csharp复制代码
hashtable.Clear(); |
请注意,由于 Hashtable 是非泛型的,因此在使用它时可能会遇到类型安全问题。如果需要类型安全,请考虑使用 Dictionary<TKey, TValue> 泛型集合。
实践
public class Person
{
private string name;
private int age;
private string address;
public Person()
{
}
public Person(string name, int age, string address)
{
this.name = name;
this.age = age;
this.address = address;
}
public string Name { get => name; set => name = value; }
public int Age { get => age; set => age = value; }
public string Address { get => address; set => address = value; }
// 重写比较方法
public override bool Equals(object obj)
{
Person person = obj as Person;
if (this.name == person.name && this.age == person.age && this.address == person.address)
{
return true;
}
return false;
}
// 重写哈希码
public override int GetHashCode()
{
// TODO: write your implementation of GetHashCode() here
return this.name.GetHashCode() + this.age.GetHashCode() + this.address.GetHashCode();
}
}
public void z1()
{
Hashtable hatable = new Hashtable();
hatable.Add(new Person("游戏",23,"日本"),"游戏王主角");
//Hashtable底层通过调用对象Equals和GetHashCode判断key是否相同;
}
泛型集合
取出数据处理的时候非常繁琐:类型判断,类型转化,逻辑处理
解决?集合本质是存储任意类型的数据
解决问题的方案就是限制集合容器只能存储具体的一个数据类型。(泛型集合)
List
#region list<T>
//是ArrayList集合的泛型集合:该集合用于存储具体的数据类型
//list<string>该泛型只能存储string类型的数据,T用于指定具体的类型,也称之为泛型。
public void z_list(){
List<string> list = new List<string>();
//增
list.Add("ddd");//[0]=ddd
list.Insert(1,"ccc");//[1]=ccc
//查
Console.WriteLine($"{list[0]}");
Console.WriteLine($"{list.Count}");
//改
list[0]="ddd";
//删
list.RemoveAt(0);
Console.WriteLine($"list[0]:{list[0]}");
//清空
list.Clear();
Console.WriteLine($"list清空后:count{list.Count}");
//存储
list.Add("aaa");
list.Add("bbb");
list.Add("cc");
list.Add("fsa");
//遍历
foreach(string i in list){
Console.Write($"[{i}] ");
}
Console.WriteLine($"");
}
#endregion stack 和 Queue
Stack和queue
//Stack<>是stack的泛型集合。
Stack<string> stack = new();
//压栈
stack.Push("1");
stack.Push("2");
stack.Push("3");
//查看数量和顶栈元素
Console.WriteLine($"{stack.Count}");
Console.WriteLine($"{stack.Peek}");
//出栈相当于删除元素
stack.Pop();
stack.Pop();
stack.Pop();
Console.WriteLine($"{stack.Count}");
//Queue<>是Queue的泛型集合。
Queue<int> queue= new Queue<int>();
//进队列
queue.Equals(1);
queue.Equals(2);
queue.Equals(3);
//查看数量和顶栈元素
Console.WriteLine($"{queue.Count}");
Console.WriteLine($"{queue.Peek}");
//出队列
Console.WriteLine($"{queue.Dequeue}");
Console.WriteLine($"{queue.Dequeue}");
Console.WriteLine($"{queue.Dequeue}");
Console.WriteLine($"{queue.Count}");
}
Dictionary
Dictionary<int , string> dir = new Dictionary<int, string>();
//增
dir.Add(1,"北京");
dir.Add(2,"上海");
dir.Add(3,"广州");
//查
Console.WriteLine($"{dir[1]}");
Console.WriteLine("dir.Count:"+dir.Count);
//删
dir.Remove(1);
Console.WriteLine($"{dir.Count()}");
//遍历
foreach (KeyValuePair<int,string> item in dir)
{
Console.WriteLine($"{item.Key}_{item.Value} ");
}
foreach (int i in dir.Keys){
Console.WriteLine($"{i}_{dir[i]} ");
}
foreach(string i in dir.Values){
Console.WriteLine($"{i} ");
}
LinkedlList
//创建一个空容器
LinkedList<string> list = new LinkedList<string>();
//增
list.AddFirst("前面个元素");
list.AddLast("后面个元素");
list.AddBefore(list.Find("后面个元素"),"中间插入的");
//查
Console.WriteLine($"{list.Count}");
//前面个元素_中间插入的_后面个元素
//System.Collections.Generic.LinkedListNode`1[System.String]
Console.WriteLine($"{list.First}");
Console.WriteLine($"{list.Last}");
//LinkedListNode包含3个属性:Previous(链接到前一个的Value),Value,Next(链接到后面的Value)
//获取LinkedListNode链接节点的格个属性
Console.WriteLine($"list.Last.Previous.Value:{list.Last.Previous.Value}");
Console.WriteLine($"list.First.Next.Value:{list.First.Next.Value}");
//通过last(后面个元素)找到first(前面个元素)
Console.WriteLine($"反向查找{list.Last.Previous.Previous.Value}");
//改
list.First.Value = "修改后前面的那个元素";
Console.WriteLine($"{list.First.Value}");
//删
list.RemoveFirst();
list.RemoveLast();
list.Remove("中间插入的");
Console.WriteLine($"{list.Count}");
//清空
list.Clear();
//遍历
list.AddFirst("前面个元素");
list.AddLast("后面个元素");
list.AddBefore(list.Find("后面个元素"),"中间插入的");
foreach(string name in list){
Console.WriteLine($"{name}");
}
自定义泛型
public class DateClass<T>
{
// 定义字段
public T date;
// 定义属性,这里使用了自动实现的属性(auto-implemented property)
public T Date { get; set; }
// 定义构造函数
public DateClass()
{
}
public DateClass(T _date)
{
this.date = _date;
}
public T ShowDate()
{
return this.date;
}
}
//自定义接口
public interface I_Move<K>
{
K Speed { get; set; }
void Move(K Speed);
}
//实现接口
public class Fish<K,N,P,W> : I_Move<K>
{
//定义自己的字段
public N name;
public P price;
public W weigh;
public K Speed { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public void Move(K Speed)
{
Console.WriteLine($"{Speed}");
}
public void Print(){
Console.WriteLine($"商品名称:{this.name} 价格:{price} 重量:{weigh}");
}
}
//定义泛型方法
public class Compute
{//泛型方法
public M DefaultValue<M>(M m)
{
return default(M);
}
}
public class J_自定义泛型
{
public void z1()
{
//泛型类的使用
DateClass<string> dateClass = new DateClass<string>("jeck");
Console.WriteLine($"{dateClass.ShowDate()}");
DateClass<int> dateClass2 = new DateClass<int>(12);
Console.WriteLine($"{dateClass2.ShowDate()}");
//使用接口
I_Move<float> i_Move = new Fish<float,string,float,float>();
//转换成Fish类型
Fish<float,string,float,float> fish = i_Move as Fish<float,string,float,float>;
fish.name = "鱼";
fish.price = 12.5f;
fish.weigh = 12.5f;
fish.Print();
//泛型方法的使用
Compute compute =new();
Console.WriteLine($"默认值{compute.DefaultValue<int>(12)}");
Console.WriteLine($"默认值{compute.DefaultValue<bool>(true)}");
//
}
泛型约束
泛型约束是通过在泛型类型定义中使用where关键字来指定的。以下是一些常见的泛型约束:
- where T : struct:该约束指定泛型类型参数T必须是值类型(如结构体、枚举或内置值类型)。
- where T : class:该约束指定泛型类型参数T必须是引用类型(如类、接口、委托或数组)。
- where T : new():该约束指定泛型类型参数T必须有一个无参数的公共构造函数。
- where T : <baseclass>:该约束指定泛型类型参数T必须是<baseclass>或<baseclass>的派生类。
- where T : <interface>:该约束指定泛型类型参数T必须实现<interface>接口。
- where T : U:该约束指定泛型类型参数T必须是泛型类型参数U或U的派生类。
- where T : U, V:该约束指定泛型类型参数T必须同时满足U和V的约束条件。
下面是一个使用泛型约束的示例:
csharp复制代码
public class MyClass<T> where T : class, IMyInterface, new() | |
{ | |
private T instance; | |
public MyClass() | |
{ | |
instance = new T(); // 由于使用了new()约束,所以可以创建T的实例 | |
} | |
public void MyMethod() | |
{ | |
// 由于T实现了IMyInterface接口,所以可以调用其方法 | |
instance.MyInterfaceMethod(); | |
} | |
} | |
public interface IMyInterface | |
{ | |
void MyInterfaceMethod(); | |
} |
在上面的示例中,MyClass是一个泛型类,其类型参数T被约束为引用类型(class)、实现了IMyInterface接口,并且具有无参数的公共构造函数(new())。这样,在MyClass内部,您可以安全地创建T的实例,并调用IMyInterface接口中的方法。
通过使用泛型约束,您可以编写更加健壮和类型安全的泛型代码,同时保持代码的灵活性和可重用性。
class Test01<T> where T : struct{}
class Test02<T> where T : class{}
class Test03<T> where T : new(){}
class Test04<T> where T : GameObject{}
class Test05<T> where T : IEnumerable{}
class Test06<T,K> where T : K{}
public class GameObject{
}
public class Cude : GameObject
{
}
public class 泛型约束
{
public void z1(){
//值类型约束
new Test01<int>();
//引用类型约束
new Test02<string>();
//无参构造函数约束
new Test03<泛型约束>();
//类,接口约束或者他们的子类约束
new Test04<GameObject>();
new Test04<Cude>();
//接口约束
new Test05<IEnumerable>();
new Test05<ArrayList>();
//泛型约束第一个类型是第二个类型的子类
new Test06<int,int>();
new Test06<string,Object>();
}
委托
普通委托(delegate)
定义:就是能把方法存入的变量、
关键字:delegate
public class Calculater
{
public void Report()
{
Console.WriteLine($"NO Report.");
}
public int Abs(int V)
{
return Math.Abs(V);
}
}
//定义委托无返回值参数的委托
public delegate void NoRetParFun();
//定义有返回值参数的委托
public delegate int RetParFun(int v);
public class 委托
{
public static void Print(){
Console.WriteLine($"MainClas.Print()");
}
public void z1()
{
Calculater calculater = new Calculater();
//1_创建无返回值的委托类型对象
NoRetParFun noRetParFun = new NoRetParFun(calculater.Report);
//1调用委托方法
noRetParFun();
//2_创建委托类型对象
NoRetParFun noRetParFun1 = calculater.Report;
//调用方法2
noRetParFun1.Invoke();
//创建有返回值的委托
RetParFun retParFun = calculater.Abs;
retParFun(10);
retParFun.Invoke(10);
//应用:可以进行函数的运算+= -=,游戏中可以当用户触发一个事件后执行多个函数业务逻辑
noRetParFun += Print;// = calculater.Report+ 委托.Print
noRetParFun();
noRetParFun -= calculater.Report;
noRetParFun();
//验证委托是一个类
Console.WriteLine($"{typeof(Calculater).IsClass}");//True
Console.WriteLine($"{typeof(int).IsClass}");//False
Console.WriteLine($"{typeof(NoRetParFun).IsClass}");//True
Console.WriteLine($"{typeof(RetParFun).IsClass}");//True
系统委托
Action无返回值的委托
Func 有返回值的委托
就是系统创建好的有返回值或无返回值的有参数列表的委托
案例
public static void Print(){
Console.WriteLine($"print");
}
public static void say(string name){
Console.WriteLine($"{name}你好");
}
public static string DDD(string name,int zhi){
return name + zhi;
}
public void z1(){
Action action = Print;
action();
Action<string> action02 = say;
action02("川哥");
Func<string,int,string> func = DDD;
Console.WriteLine($"{func("钱:",12)}");
}
事件
语法:访问修饰符号 Event 委托类型 事件名;
//1事件不能在其他类中进行初始化
//2事件不能在其他类中进行调用
//通过定义事件类中的函数进行事件间的间接调用
//事件可以+=或者-=
class Test{
//普通委托字段
public Action action01;
//事件(安全委托字段)
public event Action event01 = 事件.Print;
public void CallEvent(){
event01();
}
}
public class 事件
{
public static void Print(){
Console.WriteLine($"MainClass.Print()");
}
private void show() {
Console.WriteLine($"Text");
}
public void z1(){
Test test01 = new Test();
//普通委托
test01.action01 = Print;
test01.action01();
//1事件不能在其他类中进行初始化
//2事件不能在其他类中进行调用
//通过定义事件类中的函数进行事件间的间接调用
//事件可以+=or-=
Test test02 = new Test();
test02.event01 += show;
test02.CallEvent();
}
}
协变和逆变
Out用于返回值的泛型;
In用于参数列表的泛型;
interface I_Out<out T>
{
public T Move();
}
interface I_In<in T>
{
public void Run(T speed);
}
//委托
delegate string CustomDelegate<in V, in E,out R>(V v, E e);
public class out协变in逆变
{
public static string End(string set , char ch){
return set + ch;
}
public void zi()
{
CustomDelegate<string,char,string> custom = End;
}
}
}
匿名函数
//委托
public static Action action = delegate(){
Console.WriteLine($"匿名函数");
};
//委托加泛型
public static Action<string,int>paramAction = delegate(string name,int age){
Console.WriteLine($"{name}今年{age}岁");
};
//有返回值的事件
public static event Func<int,string>func = delegate(int num){
return num+"";
};
public void main(){
//调用
action();
//调用
paramAction("jack",28);
//调用
Console.WriteLine($"{func(12)}");
Console.WriteLine($"{func(12).GetType()}");
Lambda表达式
定义:lambda表达式是一种用于简化匿名函数的定义的表达式
作用:简化匿名函数的定义;
语法:(参数列表)=>函数体{}
相当于直接把匿名函数的delegate直接删掉再在()后面加上=>
注意:1.函数体只有一条可以省略,2.函数体只有一条语句的时候{}和return都可以省略3.
public static Action action = ()=>{
Console.WriteLine($"匿名函数");
};
//委托加泛型
public static Action<string,int>paramAction = (string name,int age)=>{
Console.WriteLine($"{name}今年{age}岁");
};
//有返回值的事件
public static event Func<int,string>func = (int num)=>{
return num+"";
};
public void main(){
//调用
action();
//调用
paramAction("jack",28);
//调用
Console.WriteLine($"{func(12)}");
Console.WriteLine($"{func(12).GetType()}");
}
委托与泛型的实战案例
让值类型和引用类型排序
引用类型修改比较函数
public class Person/*:IComparable<Person>*/{
private string name;
private int age;
public Person(string name,int age){
this.name = name;
this.age = age;
}
public string Name{get => name;set=>name = value;}
public int Age{get=>age;set=>age = value;}
//比较函数用于排序引用变量的方法
// public int CompareTo(Person other)
// {
// return this.age - other.age>0?1:-1;
// }
public override string ToString()
{
return $"名字{name}年龄{age} ";
}
}
public class 委托与泛型结合的案例
{
/*
基础:List<T> 泛型集合<int>、<double>,<string>,<Person>
排序:List<T> 提供了Sort()的函数重载可以进行排序
*/
//应用类型排序
public static void SorNullType(){
List<Person>list = new List<Person>();
list.Add(new Person("jack",32));
list.Add(new Person("luc",22));
list.Add(new Person("peter",62));
list.Add(new Person("mark",35));
//排序前
Console.WriteLine($"排序前");
Print(list);
// list.Sort(delegate (Person p1,Person p2){
// return p1.Age - p2.Age>0?1:-1;
// });
list.Sort((Person p1,Person p2)=> p1.Age - p2.Age>0?1:-1);
Console.WriteLine($"排序后");
Print(list);
}
//值类型排序
public static void SortValueType(){
List<int> list = new List<int>();
list.Add(98);
list.Add(100);
list.Add(99);
list.Add(97);
list.Add(96);
//输出遍历
Console.WriteLine($"排序前");
Print(list);
list.Sort();
Print(list);
}
public static void Print<T>(List<T> list){
foreach(T item in list){
System.Console.Write(item + " ");
}
Console.WriteLine($"");
}
public void main(){
//值类型排序
SortValueType();
//引用类型排序
SorNullType();
}
多线程(Thrad)
Thread线程名字 = new Thread(函数名);线程启动用线程名字.start();就可以了
internal class Program
{
static void Main(string[] args)
{
//创建线程对象(开辟路径)
Thread thread01 = new Thread(LargeFile);
//启动线程
thread01.Start();
//创建线程对象(开辟路径)
Thread thread02 = new Thread(PrivacyClean);
thread02.Start();
//在主路径中编写业务逻辑
int second = 20;
while (true)
{
Thread.Sleep(1 * 1000);
Console.WriteLine($"主路径{second--}");
if (second == 0)
{
Console.WriteLine($"~~主路径~~");
return;
}
}
}
public static void LargeFile()
{
string name = "大文件窗口";
int second = 15;
while (true)
{
//停1秒
Thread.Sleep(1 * 1000);
Console.WriteLine($"{name}扫描系统中的大文件.耗时:{second--}");
if (second == 0)
{
Console.WriteLine($"~~大文件窗口扫描完成~~");
return;
}
}
}
public static void PrivacyClean()
{
string name = "隐式数据窗口:";
int second = 10;
while (true)
{
//停1秒
Thread.Sleep(1 * 1000);
Console.WriteLine($"{name}扫描系统中的大文件.耗时:{second--}");
if (second == 0)
{
Console.WriteLine($"~~隐式数据窗口扫描完成~~");
return;
}
}
}
}
多线程的应用
多线程可以用lock(定义锁){锁住的函数体} 给一些共工的程序锁住,
New Thread的时候可以直接用lambda表达式直接把线程写好,在.stack执行
private static int tickets = 20;
private static bool isRun = true;
//定义对象锁
private static object customLock=new object();
static void Main(string[] args)
{
new Thread(
() => {
string name = "窗口1";
//循环卖票
while (isRun)
{
//模拟卖票过程
Thread.Sleep(1000);
lock (customLock)
{
if (tickets > 0)
{
Console.WriteLine($"{name}售出票{tickets}");
tickets--;
}
else
{
isRun = false;
Console.WriteLine($"{name}售票完毕。");
}
}
}
}
).Start();
new Thread(
() => {
string name = "窗口2";
//循环卖票
while (isRun)
{
//模拟卖票过程
Thread.Sleep(1000*2);
lock (customLock)
{
if (tickets > 0)
{
Console.WriteLine($"{name}售出票{tickets}");
tickets--;
}
else
{
isRun = false;
Console.WriteLine($"{name}售票完毕。");
}
}
}
}
).Start();
}
预处理指令
定义:预处理指令主要用于指导编译器进行编译前的信息处理
例举:#region折叠代码的预处理指令,称之为预处理指令
语法:#指令名
#define CONDITION//定义预处理指令
#undef CONDITION//取消预处理指令
要放在头文件上
//运行预处理指令(可以指定错误信息和警告信息的行号和文件)
#line 200 "Text.cs"
//错误和警告信息预处理指令(让编译器抛出一个错误和警告)
#error 我是一个错误
#warning 我是一个警告信息
反射
Type
动态获取类型信息;
声明过的都可以
/*
定义:反射就是使用代码动态获取类型信息
分析:
-类型:值类型(int,bool,struct)引用类型(string,array,自定义类)
-信息:成员(字段,属性,构造函数,函数。。。。)
Type类:
该类是一个抽象类,可以通过以下的方式获取指定类型的Type类对象:
-对象.GetType()
-typeof(命名空间.类名)
-Type.GetType("命名空间.类名")
*/
public void main(){
//获取值类型的Type类对象
Type type01 = 12.GetType();
Type type02 = typeof(System.Int32);
Type type03 = Type.GetType("System.Int32");
//判断三个Type类对象是否相同:True
Console.WriteLine(type01==type02);
Console.WriteLine(type02==type03);
//注意:一个类型的Type类对象通过不同的方式获取对象是同一个
//获取引用类型的Type类对象:object
Type type04 = new Object().GetType();
Type type05 = typeof(System.Object);
Type type06 = Type.GetType("System.Object");
Console.WriteLine($"{type04==type05}");
Console.WriteLine($"{type05==type06}");