Unity学习日志_Unity初识_C#基础注意事项
本篇博客将整理笔者学习C#基础以来认为需要注意的一些细节和事项,内容可能并不完整且可能存在错误,欢迎各位大佬前来指正。
1. 开始
-
C#严格区分大小写。
1. “=”的右侧可以为表达式,但会降低代码的可读性。
-
例:
int numA = 1,numB = 2; bool _isEqucal = numA == numB; //_isEqucal值为false
-
namespace :命名空间,包含一系列的类。对类进行逻辑划分,作用:为了避免重名。
-
using :引用xx命名空间。
-
C#格式化输出:string.Format();如何保留空字符:char a = ‘\0’;(\0也属于转义字符的一个)。String b = “”;保留空字符串。
-
局部变量必须声明赋值之后才可以使用。但是成员变量存在初值,可以声明之后直接使用。
-
C#的数据类型默认不允许使用null赋值,但在数值类型后加一个?就可以了。例如:int? = null;
-
声明object类型,可赋值所有类型(因为所有类型都是object的子类)。
-
递归能别用就别用,递归的效率相对于循环很低。
-
交错数组不属于多维数组。它属于数组的数组。
-
对于引用类型,xx[0]改的一定是堆中的数据。xx = dd;改的一定是栈中的引用。
-
使用数组作为形参的时候,可以选择使用 数组名.CopyTo(需要复制的数组名,开始复制的索引值)来创建新数组,这种方法依旧会对方法外原数组产生影响。
-
对于string和数值类型的转换永远都是tostring和Parse方法。
-
C#命名严格遵守驼峰命名法。
-
面向对象的三大特点:封装,多态,继承。
-
和多态相关的都不能是静态。因为静态不是面向对象的。
-
使用父类声明中存放着子类对象的形式,调用父类虚方法,一定会调用对应子类对象的重写方法。
-
数据类型:
2. 运算符
2. 赋值运算符
-
赋值表达式本身也有值,所以有以下写法:
int numA,numB; numA = numB = 1;
3. 算数运算符
- int型相除,结果会先被保存为int型,之后转为存放结果的变量类型。所以整形相除小数点后面的数值一定会丢失!整型和非整型相除,非整型和非整型相除没有影响。小数精度取决于非整型。
- 数值类型计算时特殊现象:byte+byte=int,byte+short=int,这两个数值范围小,很容易超出范围所以结果存为int型。Decimal不能和非整型相加减,因为存储的结构不同,,但可以和整型相加减。,其余情况两个数值类型做计算时,数值类型向大的靠拢。
4. 快捷运算符
-
快捷运算符例如“+=”,“*=”等操作实际效果如下:
int numA = 1,numB = 2; numA += numB; //numA结果为3。等价于下面操作: numA = numA + numB;
5. 一元操作符
-
numA++和++numA的区别:
int numA = 1,numB; numB = numA++; /* 效果等同于: numB = numA; numA += 1; */ numB = ++numA; /* 效果等同于: numA += 1; numB = numA; */
6. 三元操作符
-
三元操作符的语法结构:
//数据类型 变量名 = 条件?满足条件:不满足的条件; string str01 = 1 > 2 ? true:false;
7. 逻辑运算符
- 计算机短路逻辑:
- &&:对于与运算,如果第一项已经确定为假,则不在计算第二项,结果为假。
- ||:对于或运算,如果第一项已经确定为真,则不再计算第二项,结果为真。
- 总结:当进行逻辑运算时,不论是&&运算还是||运算,尽量让计算量小的占在第一计算项。这样可以减少平均计算时间。
3. 数值转换
-
显式转换:由大范围到小范围的强制转换。
-
隐式转换:由小范围到大范围的自动转换。
-
隐式转换的经典例题:
/***************/ int numA = 1; numA += 3; Console.WriteLine(numA); /***************/ byte numB = 1; numB += 3; /***************/ /* 问题:这两个是否一样? 答:不一样。因为byte+int = int,但b变量是byte类型的,需要转换成byte型进行存储。 */
-
-
拆装箱操作:值类型和引用类型之间的转换。
int numA = 123; Object tempNum = numA;//装箱操作,Object为引用类型 float numB = (float)tempNum;//拆箱操作 //注意:使用“+”直接做不同类型数据的字符串连接会导致拆装箱操作。连接字符串可用下面方法实现: String str01 = numA.toStirng()+"456";
-
数值与字符串之间的转换:(此过程不会发生拆装箱操作但属于值类型和引用类型之间的转换)
-
数值==》字符串:
数值.toString();
字符串==》数值:
想要转成的数值类型.Parse("字符串");
-
4. 内存分配
1. 内存分配:
程序运行时,CLR将申请到的内存空间逻辑划分成栈区和堆区
-
栈区:
1. 空间小,读取速度快。
2. 用于存储正在执行的方法,分配的空间叫做栈帧,栈帧中存储方法的参数以及变量等数据,方法执行完毕后,对应的栈帧会被清除。 -
堆区:
- 空间大,读取速度慢。
- 用于存储引用类型的数据。
2. 局部变量:
定义在方法内部的变量
- 特点:
- 没有默认值,必须自行设定初始值,否则不能使用。
- 方法被调用时,存在栈中,方法调用结束后从栈中清除。
- 局部变量中值类型和引用类型局部变量的区别:
- 值类型(直接存储数据):声明在栈中,数据存储在栈中。
- 引用类型(存储数据的引用):声明在栈中,数据存储在堆中。栈中存储该数据的内存地址。
3. 成员变量:
- 特点:
- 有初始值,可以声明后直接使用。
- 存在堆中。
- 类被实例化后存在堆中。类的实例化对象被回收时,成员变量从堆中被清除。(局部变量在方法被调用时生成)。
- 可以与局部变量重名。(局部变量的声明在栈中,成员变量在堆中,所以可以相互之间重名)。在一起使用的时候用this指代。
- 成员变量中值类型和引用类型局部变量的区别:
- 值类型(直接存储数据):声明在堆中,数据存储在堆中。
- 引用类型(存储数据的引用):声明在堆中,数据存储在堆中。
5. 循环语句
-
for
for循环的应用:预定次数的循环,遍历循环。累加累乘等操作。
-
while
while短路逻辑:
while(ture){
if(结束条件){
break;
}
//循环体
}
3. foreach:非常适合遍历数组
1. 语法:foreach(var item in array){
方法体
};
2. 其中item变量代表数组中的项,无法赋值,array为需要遍历操作的数组。
3. 优点:对于遍历操作十分方便。
4. 缺点:仅限于遍历方便。性能不如for循环。不能修改元素(赋值)。
6. 分支语句
- if else
- switch
- switch选择语句中,case后跟的是括号里变量或表达式的具体值。且括号里的变量不能为单纯的非整型变量(float之类)。
- 原因是switch的判断条件只能是等于,对于数据类型的精度要求比较高。字符型、布尔型、枚举型都可以转换成整型进行判断,是因为整形在相等不相等上有很大的确定性,而float和double表示的小数,毕竟精度都是有限的,超过限度以后的等于和不等于计算机就不好判定,故不能用。
- switch选择语句中,case后跟的是括号里变量或表达式的具体值。且括号里的变量不能为单纯的非整型变量(float之类)。
7. 方法
- 有返回值类型的方法的返回值只要是可兼容(可以隐式转换的)的就行。比如:float返回值类型可以返回一个int变量。
- return语句的作用:返回数据,退出方法。对于返回值类型为void的方法内部可以写return;表示退出方法。
- 方法体应当写的小,这样代码的重用率会更高。
- 书写大型方法时的处理原则:分而治之。
1. 方法重载
- 方法重载:在不同条件下解决同一类相似问题可以使用相同的方法名(参数的类型,数量不同),不同方法之间根据参数来区分。当然方法体通常来说是有区别的。这样做可以让调用者只需要记住一个方法就可以了。
2. 递归
(1) 核心思想:将问题转移给范围缩小的子问题。
(2) 优势:可以将复杂的问题简单化;可以使用代码体现数学公式的思想。
(3) 缺点:性能较差。原因是每次递归的时候都需要在内存上开辟一段新的空间,每次return返回时都需要释放内存上开辟的空间。
(4) 适用性:当解决问题时发现了相同的规模更小的子问题时,可以用递归解决。
(5) 在写递归的时候一定要注意结束递归的条件不能丢。
8. 数组
-
数组:在内存上开辟的连续的一块空间,用于存储若干个相同类型的数据。
-
数组通过索引来访问数组内的数据,从0到数组.length-1。
-
可以使用foreach来遍历数组。
-
一维数组和交错数组动态获得数组长度:array.Length();
-
二维或多维数组动态获得数组长度:
-
array.GetLength(0)获取行数。
-
array.GetLength(1)获取列数。
。。。。。。
初始化交错数组时,其元素的new运算符不可省略。
-
10. 参数
- 参数数组:
- 关键字:params
- 适用性:当所需传入的参数类型固定但数量不固定时可以使用。
- 使用参数数组和不使用参数数组的区别(Add方法为参数列表为数组的累加方法):
- 使用params:Add(1,2,3,4,5,8,45);
- 不使用params:Add(new int[]{1,2,3,4,5,8,45});
- 优势:使用params对于方法内部没有影响,对于方法外部可以传入数组,可以传入一组类型相同的数据,也可以传入一组类型相同的变量。甚至可以不传递参数。方便调用者调用方法。
- 输出参数:
- 关键字:out
- 传递实参变量的引用(地址)这一点和引用参数相同。
- 作用:返回结果。传递的实参变量本身作为了存储结果的变量。
- 特点:使用前实参变量可以不赋值。
- 值参数:
- 无关键字默认就是值参数。
- 值参数的传递就是赋值的过程,即变量中存的是什么就传递什么。
- 引用参数:
- 关键字:ref
- 传递变量的地址。
- 对于ref参数,传进来的为变量的地址,当修改时修改的是变量所直接存放的数据。
- 值参数和引用参数的区别(以new新数组为例,上图为值参数,下图为引用参数):
11. 垃圾回收器GC
1. 产生垃圾的原因:当方法执行完后,对应的栈帧会被清除,此时值类型数据会随之一起被清除,但引用类型只是清除了一个指针,对应的堆中的数据并没有清除,此时就产生了垃圾。
2. 垃圾回收时会占用大量的计算机资源,所以在实际项目开发时要尽量选择时段进行垃圾回收。
3. 一般情况下,GC会在内存空间不够的时候进行自动调用。
4. GC工作原理:GC线程会跟踪栈中的引用,如果GC无法跟踪到某一个堆中的内存,则认为这块内存是垃圾,标记为可回收(不会立即回收)。
12. string类型
1. C#中的String和string的区别:
- string是c#中的类,String是.net Framework的类(在c# IDE中不会显示蓝色) 。
- c# string映射为.net Framework的String 。
- 如果用string,编译器会把它编译成String,所以如果直接用String就可以让编译器少做一点点工作 。
- 如果使用c#,建议使用string,比较符合规范 。
- String只有在前面有using System;的时候并且当前命名空间中没有名为String的类型(class、struct、delegate、enum)的时候才代表System.String 。
- string是关键字,String不是,也就是说string不能作为类、结构、枚举、字段、变量、方法、属性的名称,而String可以 。
2. string的特性:
1. 不可变性:
1. string类型作为C#的引用类型,其内部存储的数据是不可改变的。原因是无法得知接下来要存储的字符串大小,如果强行存入则会导致内存溢出。Object类型也存在这个特性。
2. 字符串池:
1. 当一个新的字符串常量被创建时,都会先去字符串池中查看是否存在相同的文本。如果存在,则直接返回该对象的引用,否则创建新的字符串常量存储在字符串池中。目的:提高内存利用率。
13. 枚举
1. 枚举:用于使数值类型01234产生逻辑意义。相当于给01234这类数据添加了个标签。枚举类型与类同级但属于值类型,可以写在类内部和外部。
2. 当想要一次性选择多个枚举常量时:
1. 枚举值的要求:任意多个枚举值做|(按位或,也就是讲一个数转换成二进制后各个位对应做或操作)操作结果不会和其他枚举值冲突。(值以2的n次方来设置,这样可以保证做按位或操作时结果不会冲突)。
2. 定义枚举时用[flags]修饰,程序员内定的规则。
3. 当想要输入多个枚举值时,使用枚举值做|操作的方式传入。
4. 方法内部使用&(按位与)操作,&的结果不为0说明包含。
3. 枚举类型的数值转换:
1. int ==》enum,显式转换直接转。
2. enum ==》int,显式转换直接转。
3. enum ==》string,tostring()方法。
4. string ==》enum, enum.Parse方法。
14. 类和对象
-
通常每个类都在一个独立的cs文件中。
-
类中的变量默认为private,但是为了规范要自己手动敲出来。
-
this关键字:this中存放当前调用方法的对象引用。
-
对象是类的实例,调用构造函数才会产生类的实例。
Wife wife01 = new Wife();//调用构造函数之后,此时wife01成为Wife类的实例 wife01.Cooking();//此时Cooking方法中的this指代的就是wife01
- 创建的类在堆中需要内存空间的大小取决于类中声明的字段。
-
自动属性不能限制存入字段中的数据,普通的公开属性可以限制。
类的进阶应用:
- 类(结构)属性进阶应用:定义以自身类型为返回值的方法或属性。此类用法应定义在构造函数之后。原因:返回对象需要new使用构造函数,且对象内包含的数据是类(结构)的字段,所以需要放在他们后面。
1. 构造函数
1. 构造函数:构造类的对象的函数,提供创建对象的方式,常常用于初始化类的数据成员。(类中没有构造函数时,会自动提供无参构造函数)构造函数没有返回值。构造函数只在new的时候调用一次。所以适合初始化。
2. 使用构造函数初始化的时候,建议使用属性赋值,因为通常情况下属性中存在数值限制。
3. 构造函数不能被外部手动调用,但构造函数可以调用构造函数。
4. 在构造函数后面加:this(参数列表)就可以调用对应的构造函数。
2. 访问修饰符
1. public:类的内部和外部都可以访问。
2. internal:同一程序集(namespace)的可以访问,程序集外的不能访问。
3. protected internal:类的内部,继承该类的子类或者另一程序集继承该类的类可以访问。
4. protected:类的内部和继承该类的子类可以访问。
5. private:类的内部可以访问。
15. 继承
- 继承的特点:
2. 子类可以继承父类成员,但只能使用父类的非私有成员。(私有成员会被继承,但受限于修饰符限制子类无法使用)
3. 父类不可以使用子类成员。
4. 继承时基类的构造函数不被继承。
5. 父类引用可以指向子类对象。但只能使用父类成员,或者子类的重写方法。 - 适用性:不同类之间存在相同的代码,则将相同的代码提取出来作为这几个类的父类。
- 其他叫法:父类也叫基类,子类也叫派生类。
- 继承的特性:
- 单根性:一个派生类只能有一个基类(可以有多个接口)
- 传递性:可以将继承想成树结构,子节点的类可以继承向上回溯所有父节点类的非私有字段。
- base关键字:由于基类在子类被创建时会在子类对象中创建基类对象,base指的就是这个基类对象(base指向直接基类),是这个子类对象独有的。
- 一个类或者结构体只能继承一个基类,但可以继承多个接口。
关于构造函数
- 关于构造函数的继承:首先需要明确的是,子类并没有继承父类的构造函数。因为如果父类中存在私有字段,子类的实例就无法对父类的私有字段赋值。
- 问题解决:C#中,当创建子类对象的时候会调用子类的构造函数,这时候C#会从扩充类以此向上寻找基类,当找到最初的基类后,依次向下执行无参构造函数,最后执行子类的构造函数。原因:子类想要调用父类的成员就需要有父类对象。所以要调用父类的构造函数。
- 例子:有A,B,C,D四个类,且A->B->C->D(D为基类)则创建A对象的时候构造函数的执行顺序为:D,C,B,A。
- 如果new的是子类的全参构造函数需要通过:base(参数列表)调用父类的全参构造函数。
16. 静态(包含静态和实例的一些比较)
1. 静态成员变量:
- 静态成员变量:在类中,带有static修饰符的字段为静态成员变量。类中分为实例成员变量和静态成员变量。
- 静态成员变量属于类,类在被加载时初始化(在C#中类名一旦被提及,对应的类就会被加载装入内存),且只有一份存储在内存中的“静态区”。
- 实例成员变量属于对象,在每一个对象被创建时初始化(对象创建时需要开辟多大的空间取决于实例成员变量),且每个对象一份。
- 特点:存在优先于对象,被所有对象共享,常驻内存。
2. 静态构造函数:
- 静态构造函数&(实例构造函数):
1. 初始化类的静态数据成员。(初始化类的实例数据成员)
2. 仅在类被加载时调用一次。(仅在创建对象时调用一次)
3. 不能使用访问修饰符。(可以使用访问修饰符,一般为public)
3. 静态方法不能访问实例成员:
- 静态方法只能访问静态成员:当对象调用实例化方法时会传递对象自身的引用,这样方法才能知道是谁调用了自己。并可以使用实例成员。而静态方法是类的方法,调用时不需要引用,故不能访问实例成员。实例方法在被调用时才会被装入内存。
4. 优点:
- 单独空间存储
- 可被本类的所有对象共享
- 可直接被类名调用
5. 缺点:
- 静态方法只能访问静态成员
- 共享时会出现并发。
17. 密封类
1. 关键字:sealed
2. 用途:
1. 防止继承的滥用而导致项目结构过于复杂。
2. 防止重写方法被再次改写。
3. 注意:
1. 密封类中不能包含虚方法(Virtual)和抽象方法(abstract),因为在密封的类没有为派生类提供实现其虚方法和抽象方法的机会。如果想要密封这个类,这个类必须是“完整的”。
2. 密封方法必须是重写方法。
3. 静态不能密封。
18. 结构体
1. 语法:与类基本相同。
- 使用关键词定义(结构:struct,类:class)。
- 语法结构包含字段,属性,构造函数,方法。
- 非带有static,const修饰的字段不可直接初始化。(由于类相对于构造体更为常用,所以在类中编译器会根据习惯来自动为开发者做一些事情)
- 构造函数中需要初始化所有字段或者不做初始化。
2. 结构体相对于类的区别:
- 结构是值类型,直接存储数据;类是引用类型,间接存储数据。
- 结构的无参构造函数不能写,编译器自带。
- 结构中如果要使用自动属性,需要构造函数调用无参构造函数来辅助实现。
- 结构不能继承,但可以实现接口。
3. 适用性:
- 表示坐标,颜色(RGBA)等轻量级对象,原因是方法调用结构的时候,结构对象的值存储在栈(空间小,不适合存储大量变量)中。
- 表示坐标这种变换频繁的变量。因为方法中的结构对象存储在栈中,频繁改变不会产生垃圾。
19. 抽象类
1. 关键字:abstract
2. 抽象类只能做其他类的基类。不能实例化。
3. 派生类需要实现所有的抽象方法,普通方法不用管。
4. 当基类中有方法不确定方法体时就可以使用抽象类。
20. 接口
接口特点:
- 接口完全抽象,内部不可以有实现的方法。
- 一个类可以继承一个基类和多个接口。
- 接口适合用来做小而简炼的功能。
- 接口命名:IXXXable。
- 接口中只能包含方法,属性,索引器和事件声明,不能包含构造函数,也不能包含字段。
- 接口中都隐式声明为public(类里默认private),不能再用public声明。
- 接口可以用类或者结构实现。
- 接口不能实例化。
- 接口只能继承接口,类可以继承接口也可以继承类。
- 接口可以多继承,可以用一个接口将多个需要继承接口集合在一起,再由类去继承。
- 继承接口的方法必须全部实现,而继承抽象类则需要实现所有抽象方法。
显示实现接口:解决方法重名问题。
- 实现接口方法时命名采用:接口名.方法名。
接口的作用:
- 代码通过接口定义来表明自己对外开放的能力,具体的实现则交给实现这个接口的类。只要实现了这个接口的类,都一定存在对应的方法,那么我拿到这个类的实例对象的时候,就一定可以调用这个对象所实现的方法。
21. 多态
多态:C#中关于多态的定义是同一操作(相同的方法)可作用于不同类的实例,且有不同的执行结果。
1. 实现多态1:虚拟方法和重写方法。
-
在基类中用virtual标记的方法称之为虚拟方法,可以在扩充类中使用override重写此方法。
- 重写的方法的返回值类型,参数个数,类型,方法名称都需要和虚拟方法一致。
- 虚拟方法不能是static类型,因为多态是面向对象的需要实例化。
- 虚拟方法不可以是private类型。因为子类不能访问父类的私有对象。
-
补充:new隐藏方法:
- 当基类无法被修改但又不是虚方法,需要使用new来隐藏基类中的同名方法。
2. 实现多态2:实现抽象类。
3. 实现多态3:实现接口。
22. 泛型
1. C#泛型集合类:集合类型<数据类型>,比如List<User>,c#泛型集合类本质是对特定类型数组的封装,直接操作数组比较麻烦,所以就把一些特定功能和数组一起封装在了一个集合类中。集合类型包括:List<Type>列表,SoredList<TKey,TValue>排序列表,Dictionary<TKey,TValue>字典,Queue<Type>队列,Stack<Type>栈。
2. 强烈推荐使用泛型集合类而不使用非泛型集合类,虽然两者都是对于数组的封装,但非泛型集合类没有指定数据类型,这导致了频繁的拆装箱操作和不安全数据类型问题,泛型集合通过指定数据类型很好的解决了这两个问题。
where
-
where用于限定泛型的范围。
-
可以限定特定类型where T :int
-
可以限定是引用类型还是值类型:
- where T :class//限定引用类型
- where T :struct//限定值类型
-
可以限定构造函数的类型:
- where T :new()//类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。
-
可以是限定为一个接口:
- where T :Iinterface//其中T必须是一个实现了接口的类型
-
可用于泛型类,泛型方法,泛型委托
23. 委托&事件
委托(引用类型):
-
特点:所有的方法都可以通过委托来调用。委托相当于是C++中的函数指针,存放方法的引用。
-
可以将委托理解成方法的多态,同一个委托可以调用不同的返回值类型,参数相同的方法。
-
语法:
- [访问修饰符] delegate 返回类型 委托名(参数列表);
-
通过委托调用方法:
- 直接使用方法名,前提是该方法与传入的委托类型有相同的返回值类型和参数列表。
- 实例化委托(将方法引用存放到委托中),调用委托。
- 注意这时候传进来的对象是一个方法。
-
通过委托可以调用静态方法,也可以调用实例方法。
事件(本质还是委托):
事件的本质是委托,其是利用委托来实现的,所以和对应委托有相同的返回值类型和参数列表。同时注册事件的方法也需要有相同的返回值类型和参数列表。注:这导致事件和其委托返回值类型为void。
-
事件声明:
- 先声明一个委托:
- public delegate void MyEventHandle();
- 利用委托声明事件:
- public event MyEventHandle MyEvent;
- 先声明一个委托:
-
事件注册:
- 使用 “+=” 注册,使用 “-=” 取消注册。注册的方法可以为委托。单委托在注册的时候需要实例化。注册的过程其实就是将引用传给事件的过程。被注册的方法叫做事件处理方法。
-
事件被触发的过程:
- 事件触发–》调用OnXXX方法–》调用事件–》根据注册事件调用对应的事件处理方法。
- 其中OnXXX方法需要和被监测对象(可以是属性(通常用于监测字段变化),可以是方法)放在一起。
- 一旦调用了OnXXX方法,后面就会连锁调用。
-
具有标准签名的事件:
- 在绝大多数情况下使用的都是具有标准签名的事件。
- public delegate void EventHandler(object sender, EventArgs e);
- public delegate void EventHandler (object sender, TEventArgs e);
- 其中EventHandler用于不包含事件数据的事件,EventHandler用于包含事件信息的事件。如果没有事件数据,可将第二个参数设置为EventArgs.Empty,否则第二个参数类型需要从EventArgs类继承。
- 在绝大多数情况下使用的都是具有标准签名的事件。
24. Lamda表达式
-
语法:
- (参数列表)=>表达式/{语句块};
- Lambda表达式或者语句块是没有return。
-
Func委托和Action委托使用Lambda表达式:
- Func委托和Action委托都属于官方提供的泛型委托,一般直接拿来用就可以,不需要自己定义新的委托。
- Func委托是带返回参数的委托,Action委托则是void委托。
//正常委托实例化应该是这样的:
Func<int,int> MyFunc1 = new Func<int,int>(Class.Function);
//使用Lambda实例化:
Func<int,int> MyFunc2 = n => n*3;
//使用匿名委托:
Func<int,int> MyFunc3 = delegate(){return Class.Function};
//所以lambda表达式YES!
ntArgs e);
2. 其中EventHandler用于不包含事件数据的事件,EventHandler用于包含事件信息的事件。如果没有事件数据,可将第二个参数设置为EventArgs.Empty,否则第二个参数类型需要从EventArgs类继承。
24. Lamda表达式
-
语法:
- (参数列表)=>表达式/{语句块};
- Lambda表达式或者语句块是没有return。
-
Func委托和Action委托使用Lambda表达式:
- Func委托和Action委托都属于官方提供的泛型委托,一般直接拿来用就可以,不需要自己定义新的委托。
- Func委托是带返回参数的委托,Action委托则是void委托。
//正常委托实例化应该是这样的:
Func<int,int> MyFunc1 = new Func<int,int>(Class.Function);
//使用Lambda实例化:
Func<int,int> MyFunc2 = n => n*3;
//使用匿名委托:
Func<int,int> MyFunc3 = delegate(){return Class.Function};
//所以lambda表达式YES!