C#
2020-03-05 10:46:11 2 举报
AI智能生成
c#语法精要
作者其他创作
大纲/内容
类型和存储
类型
值类型
预定义类型
11种数值类型
不同长度的有符号和无符号整数类型
sbyte byte short ushort int uint long ulong
浮点数类型
float
double
高精度小数类型 decimal
Unicode字符类型 char
布尔类型 bool
用户定义类型
struct
enum
引用类型
预定义类型
object
string
dynamic
用户定义类型
class
interface
delegate
array
存储
内存区域
栈
堆
数据类型按存储方式划分
值类型
只需要一段单独的内存,存储实际数据
引用类型
需要两段内存
第一段存储实际的数据,总是位于堆中
第二段是一个引用,指向数据在堆中的存放位置
存储引用类型对象的成员
引用类型对象的数据部分始终存放在堆中
数据部分无论是值类型还是引用类型
数据部分是引用类型的,其引用部分也存放在堆中
值类型对象,或引用类型数据的引用部分可以存放在堆里,也可以存放在栈里
类和继承
所有类隐式继承自Object
只支持单继承
屏蔽基类的成员
屏蔽数据成员
声明一个新的相同类型和名称的成员
屏蔽函数成员
声明新的带有相同签名的函数成员,签名不包含返回类型
使用new修饰符通知编译器是故意屏蔽的
也可以屏蔽静态成员
基类访问
base.Field
使用基类的引用
使用类型转换运算符把派生类对象的引用转换为基类类型
MyBaseClass mybc = (MyBaseClass)derived; //注:经测试,向上转型不需要转换
虚方法和override
可以使用基类引用调用派生类的方法(多态)
派生类方法和基类方法有相同的签名和返回类型
基类方法使用virtual标注
派生类方法使用override标注
覆写和被覆写的方法必须有相同的可访问性
不能覆写static方法或非虚方法
方法,属性,索引器,事件都支持 覆写
多层继承时,可以多次override,直到使用new则不能继续override
构造函数执行
首先初始化实例成员
第二步,调用基类构造函数
最后执行该类自己的构造函数
构造函数初始化语句
构造对象时,默认调用基类的无参数构造函数
如果希望派生类使用一个指定的基类构造函数,必须在构造函数初始化语句中指定
base
也可指定使用当前类的某个构造函数
this
类的访问修饰符只有两种
public
可被系统内任何程序集中的代码访问
internal
只能被它自己所在的程序集内的类看到
默认
密封类只能被用作独立的类,不能被用作基类
抽象成员
设计为被覆写的函数成员
必须是函数成员,不能是字段和常量
用abstract修饰符标记
不能有实现代码块
只可以在抽象类中声明
抽象类
设计为被继承的类
只能被用作其他类的基类
不能创建实例
可以包含抽象成员或普通的非抽象成员
抽象类自己可派生自另一个抽象类
密封类
使用sealed修饰符标注
静态类
所有成员都是静态的
标记为static
可以有一个静态构造函数,不能有实例构造函数
静态类是隐式密封的,不能被继承
给已有类扩展方法
声明扩展方法的类必须是static
扩展方法本身必须声明为static
扩展方法必须包含关键字this作为它的第一个参数类型,并且在后面跟着它所扩展的类的名称
结构
和类的区别
结构是值类型,而类是引用类型
结构是隐式密封的,不能被派生
结构有一个隐式的无参构造函数,且不能删除或重定义
不允许有字段初始化
结构是值类型
结构类型的变量不能为null
两个结构变量不可能是同一个对象
对结构赋值
复制成员的值
装箱和拆箱
如果将一个结构实例作为引用类型对象,必须创建装箱 boxing 的副本
装箱的过程就是制作值类型变量的引用类型副本
结构作为返回值
将创建它的副本并从函数返回
值参数
当结构被用作值参数时,将创建实参结构的副本
ref和out参数
如果把一个结构用作ref或out参数,传入方法的是该结构的一个引用,这样就可以修改其数据成员
数组
一维数组
声明:long[] array;
实例化:int[] arr = new int[4];
显式初始化:int[] intArr = new int[] { 10, 20, 30, 40 };
快捷语法: int[] intArr = {10,20,30,40};
多维数组
矩形数组
子数组长度相同
使用一组方括号访问 int x = myArray[4,6,1]
声明:int[,] arr;
实例化: int[,,] arr3 = new int[3,6,2];
显式初始化:int[,] intArray2 = new int[,] { {10,1}, {2,10}, {11,9} };
快捷语法:int[,] intArray2 = { {10,1}, {2,10}, {11,9} };
交错数组
每一个子数组都是独立数组
子数组可以有不同长度
为数组的每一个维度使用一对方括号
jagArray[2][7][4]
声明并创建:int[][] jagArr = new int[3][];
数组是对象
System.Array
Rank,返回数组维度
Length 返回数组长度
数组是引用类型
数组的元素可以是值类型也可以是引用类型
一律放堆里
数组协变
Clone方法:浅复制
返回object引用,需强制转换成数组类型
类
类的成员
数据成员
字段
readonly修饰符
和const的区别
既能在字段的声明语句中初始化,也能在构造函数中初始化
const的值必须在编译时决定,而readonly字段可以在运行时决定
readonly可以是实例字段,也可以是静态字段,在内存中有存储位置
成员常量 const
表现的像静态成员,但是没有存储位置,是在编译期计算值
函数成员
方法
必须有返回类型,包括void
本地变量
必须声明在块内部
本地变量和实例字段的对比
类型推断和 var 关键字
var只能用于本地变量,不能用于字段
只能在变量声明中包含初始化时使用
一旦编译器推断出变量的类型,类型就是固定的不能更改
本地变量有效范围内不能再声明另一个同名的本地变量
本地常量
const Type Identifier = Value;
必须声明在块内部
声明时必须初始化
初始化值必须在编译期决定
通常是预定义简单类型或其组成的表达式
可以是null引用
不能是对象的引用,因为对象是运行时决定的
声明后不能改变
参数
形参
声明在方法参数列表中的本地变量
形参在方法开始之前初始化
实参
用于初始化形参的表达式或变量称作实参(argument)
实参位于方法调用的参数列表中
实参必须与对应形参的类型匹配,或是编译器能够把实参隐式转换为形参类型
4种参数类型
值参数(默认)
用法
实参可以为变量
变量必须被赋值
引用类型可赋值为一个实际的引用或null
实参也可以是任何能计算成相应数据类型的表达式
系统操作
系统在栈中为形参分配空间
将实参的值复制给形参
引用类型的引用被复制
实参和形参都引用堆中的同一个对象
值类型的值被复制
产生了一个独立的数据项
引用参数(ref )
用法
必须在方法的声明和调用中都使用ref修饰符
void MyMethod(ref int val){ ... } //方法声明
int y = 1; //实参变量,且必须赋值 MyMethod(ref y); //方法调用
实参必须是变量,在用作实参前必须被赋值
错误,实参不是变量:MyMethod(ref 3+5);
引用类型可以赋值为一个引用或null
系统操作
不会为形参在栈上分配内存
形参的参数名将作为实参变量的别名,指向相同的内存位置
输出参数(out )
用于从方法体内把数据传出到调用代码
行为与引用参数类似
用法
必须在声明和调用中都使用修饰符 out
实参必须是变量,而不能是表达式
因为方法需要内存位置保存返回值
系统操作
同引用参数
与引用参数的区别
参数的初始值是无关的,没有必要在方法调用之前为实参赋值
在方法内部,输出参数在能够被读取之前必须被赋值
在方法返回之前,方法内部贯穿的任何可能路径都必须为所有输出参数进行一次赋值
参数数组 (params)
允许零个或多个实参对应一个特殊的形参
性质
在一个参数列表中只能有一个参数数组
如果有,它必须是列表中的最后一个
由参数数组表示的所有参数都必须具有相同的类型
用法
形参声明
在数据类型前使用params修饰符
在数据类型后放置一组空的方括号
例如:void ListInts( params int[] inVals) {...}
形参inVals可代表零个或多个int实参
方法调用
方法1:列表
ListInts(10,20,30);
称为延伸式
使用分离的实参
系统操作
接受实参列表,用它们在堆中创建并初始化一个数组
把数组的引用保存到栈中的形参里
如果对应的形参数组的位置没有实参,编译器会创建一个有零个元素的数组来使用
重要
当数组在堆中被创建时,实参的值被复制到数组中
这方面,像值参数
如果数组参数是值类型,那么值被复制
实参不受方法内部的影响
如果数组参数是引用类型,那么引用被复制
实参引用的对象可以受到方法内部的影响
方法2:一维数组
int[] intArray = {1,2,3}; ListInts(intArray);
系统操作
编译器使用你的数组而不是重新创建一个
实参数组的引用被复制给形参
如果传入null,则形参也是null,因此要判断形参是否不为null
重要
无论数组参数是值类型还是引用类型,实参都可以受到方法内部的影响
但是形参重新指向一个新的数组不会影响实参
注意:调用时不允许有params修饰符
参数的调用方法
位置参数
数量一致,类型一一对应
命名参数
显式指定参数的名字,就可以以任意顺序在方法调用中列出实参
细节
方法声明没什么不一样,形参已经有名字了
调用方法时,形参的名字后跟着冒号和实际参数值或表达式
例如:c.Calc(c:2, a:4, b:3);
混合使用位置参数和命名参数
位置参数必须先列出
例如:mc.Calc(4, b:3, c:2);
可选参数
声明时为参数提供默认值
指定默认值语法和初始化本地变量的语法一样
使用条件
只有值类型的默认值在编译时可以确定
且只能是值参数,不能是ref, out, params
引用类型只允许null的默认值
且不能是ref, out, params
所有必填参数必须在可选参数声明之前声明
如果有params参数,必须在所有可选参数之后声明
调用时必须从可选参数列表的最后开始省略,一直到列表开头
如果需要随意省略可选参数,需要使用命名参数来消除赋值的歧义
方法重载 Method overload
一个类中可以有一个以上的方法拥有相同的名称
使用相同名称的每个方法必须有一个和其他方法不相同的签名 (signature)
签名组成
方法的名称
参数的数目
参数的数据类型和顺序
参数修饰符
签名不包括
返回类型
形参名称
注意:此概念与继承中“方法覆写 method override”不同
属性
set 访问器
拥有一个单独的,隐式的值参,名称为value,类型与属性的类型相同
get 访问器
没有参数,拥有一个与属性类型相同的返回类型
属性和关联字段
属性不分配内存
通常使用一个private的字段作为存储
也称为后备字段或后备存储
只读和只写属性
只有get访问器的属性只读
只有set访问器的属性只写
属性比公共字段更好
属性是函数成员,可以处理输入输出
属性可以只读或只写
编译后如果想把公共字段改成属性,访问该字段的其他程序集需要重新编译
自动实现属性
允许只声明属性而不声明后备字段
编译器会自动创建隐藏的后备字段
不能提供访问器的方法体
public int MyValue { set; get; }
实现只读和只写属性没有意义,因此必须同时提供get; set;
静态属性
构造函数
实例构造函数
名称和类名相同
不能有返回类型
可以带参数,参数语法和其他方法完全相同
可以被重载
默认构造函数
没有参数
方法体为空
如果定义了至少一个显示构造函数,则编译器不会创建默认构造函数
也可以添加访问修饰符
静态构造函数
初始化类级别的项
类只能有一个静态构造函数,且不能带参数
不能有访问修饰符
不能从程序中显示调用静态构造函数,系统会自动调用它们
在引用任何静态成员之前
在创建类的任何实例之前
析构函数
运算符
索引器
声明索引器
索引器没有名称,名称位置是关键字this
参数列表在方括号中
参数列表中必须至少声明一个参数
和属性声明的比较
索引器重载
参数列表不同 (而不是类型不同)
事件
实例化对象
new Typename()
园括号是必须的,可能包含参数
new运算符为任意指定类型的实例分配并初始化内存
new依据类型的不同从栈或堆里分配
对象初始化语句
new表达式的尾部放置了一组成员初始化语句
允许你在创建新的对象实例时,设置字段和属性的值
语法有两种形式
包括构造函数的参数列表
new TypeName(ArgList) { FieldOrProp = value, FieldOrProp = value, ... }
不包括参数列表(也没有圆括号)
new TypeName { FieldOrProp = value, FieldOrProp = value, ... }
重要说明
创建对象的代码必须能够访问要初始化的字段和属性
初始化发生在构造方法执行之后,因为构造方法中设置的值可能会在之后的对象初始化中被改变
访问修饰符
成员的访问修饰符
private
只能在类内部访问
默认是private
public
protected
派生类可访问
派生类甚至可以在不同程序集中
internal
对程序集内所有类可见
protected internal
protected和internal的并集
访问器的访问修饰符
属性和索引器带set, get访问器
默认情况下,两个访问器有和成员自身相同的访问级别
仅当成员(属性或索引器)既有get也有set访问器时,其访问器才能有访问修饰符
虽然两个访问器都必须出现,但它们中只能有一个有访问修饰符
访问器的访问修饰符必须比成员的访问级别有更严格的限制性
即只能更严格
可以声明为static的类成员
数据成员
字段
类型
函数成员
方法
属性
构造函数
运算符
事件
this关键字
在类中使用,是对当前实例的引用
目的
用于区分类的成员和本地变量或参数
作为调用方法的实参
允许使用的代码块
实例构造函数
实例方法
属性和索引器的实例访问器
分部
分部类和分部类型
类的声明可以分割成几个分部类的声明
每个分部类的声明都含有一些类成员的声明
类的分部类声明可以在同一个文件中也可以在不同文件中
partial class
partial struct
partial interface
组成类的所有分部类声明必须在一起编译
分部方法
方法的定义声明和实现声明可以分开,在同一个类中也可以在不同的分部类中
返回类型必须是void
不能包含访问修饰符,因此分部方法是隐式私有的
参数列表不能包含out参数
在定义声明和实现声明中都必须包含上下文关键字partial, 且在void之前
可以有定义而无实现,编译器会去掉方法的声明以及调用
变量
本地变量:无自动初始化
类字段:有自动初始化
结构字段:有自动初始化
参数:无自动初始化
数组元素:有自动初始化
表达式和运算符
比较操作和相等性操作
引用对象比较
深比较
string类型对象的比较,会比较长度和内容
委托也是深比较
两个委托都是null,或两者的调用列表中有相同数目的成员,并且调用列表相匹配
浅比较
其他大部分引用对象,只比较引用是否指向同一个内存对象
比较数值表达式时,将比较类型和值
比较enum类型时,比较操作数的实际值
运算符重载
只能用于类和结构
一元运算符重载
可重载: + - ! ~ ++ -- true false
二元运算符重载
可重载:+ - * / % & | ^ << >> == != > < >= <=
枚举
值类型
只有一种类型的成员:命名的整数值常量
例子
每个枚举类型都有一个底层的整数类型,默认为 int
可设置底层类型
使用枚举实现位标志
HasFlag
委托
可以把delegate看作一个包含有序方法列表的对象,这些方法有相同的签名和返回类型
委托保存的方法来自任何类或结构
方法可以是实例方法也可以是静态方法
声明委托类型
delegate void MyDel(int x);
声明委托变量
MyDel delVar;
创建委托对象
方法一:使用new
delVar = new MyDel(myInstObj.MyM1);
delVar = new MyDel(SClass.OtherM2);
快捷语法
delVar = myInstObj.MyM1;
delVar = SClass.OtherM2;
给委托赋值
引用类型,赋值改变包含在委托变量中的引用,旧的委托对象会被GC
组合委托
委托是恒定的,委托对象被创建后不能再被销毁
组合委托以及+=其实都是创建了一个新的委托对象
使用 + 号组合委托,创建了一个新的委托
为委托添加方法
注意委托是恒定的,其实是生成了一个新的委托
使用+=
方法可以重复添加,会有多个实例,调用时也会调用多次
从委托移除方法
使用-=, 创建了一个新的委托
如果在调用列表中的方法有多个实例,-=运算符将从列表最后开始搜索,并移除第一个与方法匹配的实例
试图删除委托中不存在的方法没有效果
试图调用空委托会抛出异常
可将委托与null进行比较来判断是否为空
调用委托
和调用函数类似
调用带返回值的委托
调用列表中最后一个方法返回的值就是委托调用返回的值
调用列表中其他方法的返回值被忽略
调用带引用参数的委托
参数的新值(不是初始值)会传给下一个方法
匿名方法
使用场景
声明委托变量时作为初始化表达式
组合委托时在赋值语句右边
+=时在赋值语句的右边
匿名方法不会显式声明返回类型
代码实现时必须符合委托的返回类型
参数
省略参数或圆括号
委托的参数列表不包含out参数
匿名方法不使用任何参数
params参数
委托类型声明中指定最后一个参数为params参数
匿名方法列表中忽略params关键字
变量和参数的作用域
外部变量
匿名方法可以访问它们外围作用域的局部变量和环境
外围作用域的变量叫外部变量
用在匿名方法实现代码中的外部变量称为被方法捕获
捕获变量的生命周期的扩展
只要捕获方法还是委托的一部分,即便变量已经离开了作用域,捕获的外部变量也会一直有效
Lambda表达式
简化了匿名方法的语法
删除了delegate关键字
在参数列表和匿名方法主体之间放入Lambda运算符 =》,读作goes to
进一步简化
省略类型参数
称为隐式类型
如果只有一个隐式类型参数,可省略周围的圆括号
允许表达式的主体是语句块或表达式
如果语句块包含了一个返回语句,可以替换为return之后的表达式
Lambda表达式参数列表要点
参数列表中的参数必须在参数数量,类型和位置上与委托相匹配
参数不一定需要包含类型(隐式类型),除非委托有ref或out参数,此时必须注明类型(显式类型)
如果只有一个参数,且是隐式类型的,圆括号可以省略,否则必须有括号
如果没有参数,必须使用一组空的圆括号
收藏
0 条评论
下一页