new关键字:
1)作为运算符,用于创建对象和调用构造函数。
2)作为修饰符,用于向基类成员隐藏继承成员。
实现派生类中隐藏方法,则基类方法必须定义为virtual,在派生类中通过
new或override进行灵活控制。而new和override不可共存,因为new用于实现创建一个新成员,同事隐藏基类的同名成员,而override用于实现对基类成员的扩展。在子类中隐藏了基类成员,若要对基类成员进行访问可以通过base来完成。
3)作为约束,用于在泛型声明中约束可能用作类型参数的参数类型。
new作为约束和其他约束共存时,必须在最后指定。
new一个class时,new完成了以下两个方面的内容:一是调用newobj命令来为实例
在托管堆中分配内存;二是调用构造函数来实现对象初始化。
new一个struct时,new运算符用于调用其构造函数,完成实例的初始化。
new一个int时,new运算符用于初始化其值为0。
new运算符不可重载。
new分配内存失败,将引发OutOfMemoryException。
值类型与引用类型在分配内存时是不同的:
值类型分配于线程的堆栈(stack)上,并变量本身就保存其值,因此不受GC的控
制;引用类型变量,包含了指向托管堆的引用,内存分配于托管堆上,内存收集由GC完成。
base关键字:
调用基类上已被其他方法重写的方法。
指定创建派生类实例时应调用的积累构造函数。
this关键字:
限定被相似的名称隐藏的成员。
将对象作为参数传递到其他方法。
声明索引器。
base的应用:
常用于在派生类对象初始化时和基类进行通信。
可以访问基类的公有成员和受保护成员,私有成员是不可访问的。
可以指向的父类的方法有两种情况:一是有重载存在的情况下,base将指向直接
继承的父类成员的方法;二是在没有重载存在的情况下,base可以指向任何上级
父类的公有或者受保护方法。
this的应用:
指代类对象本身,用于访问本类的所有常量、字段、属性和方法成员,而且不管
访问元素师任何访问级别。不能在静态方法中引用this。
通用规则:
除了决议子类的名称冲突和在一个构造函数中调用其他的构造函数外,base和
this的使用容易引起不必要的结果。
在静态成员中使用base和this都是不允许的。
base是为了实现多态而设计的。
不可同时将this和base作用在一个构造函数上。
base用于在派生类中访问重写的基类成员;this用于访问本类的成员。
除了base,访问基类成员的另外一种方式是:显式的类型转换来实现,但该方法
不能为静态方法。
using的多重身份:
引入命名空间:using作为引入命名空间指令的用法规则为:using Namespace;
创建别名:using为命名空间创建别名的用法规则为:using alias = namespace|type; 其中alias为别名
创建别名的重要原因在于同一cs文件中引入的不同命名空间中包括了相同名称的
类型,为了避免出现名称冲突可以通过设定别名来解决。Dispose模式是.NET提供的一种显式清理对象资源的约定方式,用于在.NET中释放对象封装的非托管资源。
using只能用于实现了IDisposable接口的类型。
using语句声明的局部变量,必须是实现了IDisposable接口或者可以转换为IDisposable接口的类型。
using语句适用于清理单个非托管堆资源的情况,多个非托管对象的清理最好以try-finally来实现。
using语句支持初始化多个变量,但前提是这些变量的类型必须相同。
Dispose方法用于清理对象封装的非托管资源,而不是释放对象的内存,对象的内存永远由垃圾回收器控制。
程序在达到using语句末尾时退出using块,而如果到达语句末尾之前引入异常则有可能提前退出。
转换运算符的实现规则:static 访问修饰操作符 转换修饰操作符 operator 类型(参数列表);
explicit,用于声明必须强制转换的自定义类型转换操作符。
inplicit,用于声明隐式的自定义类型转换操作符。
所有的转换都必须是static的。
类型转换可能存在信息丢失或者精度损失,对于有损转换,最好提供显式的类型转换,并且这种有损转换不被允许时,应在显式转换中抛出
InvalidCastException异常。
隐式转换较为灵活,可读性强,但是必须保证 不会引发异常或者发生有损转换。
运算符只能按值传递,不能按引用传递,因此这里也不能采用ref或out参数。
预处理指令——表6-1:
以#符号开头,且必须独占源代码的单独一行,不用分号结束,只用于控制编译过程。
编译器遇到#if指令则会首先检查#if后的符号是否已经定义,如果存在定义则会执行子块中的代码,否则将忽略这段代码而继续执行,#elif的判断过程和#if是一样,直到#endif为止。
#define和#undef分别用于定义和取消定义一个给定名称的符号,该符号可以应用于#if指令作为判断条件,和其他指令搭配才有意义。且此2个指令的定义必须放在C#源代码的开头。
#warning指令用于发出警告信息,#error指令用于发出错误信息。编译器遇到
#warning指令显示警告指令后,会继续执行;而#error指令显示错误信息后,会退出编译。
#line主要应用于发布警告或者错误信息时来控制行号使用,用于修改编译器的行号或者修改警告和错误信息的文件名输出:
#line lineNo 或 hidden 或 default
lineNo表示文件行号,hidden表示隐藏连续行,default表示重置文件行号。
使用/checked命令行开关进行溢出控制,而默认的选项是/checked-,表示关闭溢出检查。当溢出检查启用时,一旦发生溢出,CLR会抛出OverflowException异常。
关键字checked和unchecked既可以是操作符,也可以是语句,checked和unchecked只能直接对整数运算和类型转换有效,而对于调用方法不会产生作用。
yield用于向枚举器对象提供值或者发出结束信号。yield return用于依次返回每个元素,而yield break用于终止枚举。
yield语句不允许应用于不安全块中。
yield return不支持方法带有ref或者out参数。
yield语句不能应用于匿名方法中。
yield语句不能出现在catch块,或者包含catch子句的try块中,也不能出现在
finally块中。lock语句用于给对象获取互斥锁,执行操作语句,然后再释放该锁。在线程同步时,lock关键字将语句块标记为临界区,能 保证代码顺利执行而不被其他线程中断,变量被包装在独占锁中,其他线程的只能等待执行解锁之后才可访问该对象。
lock的对象必须是引用类型参数,最好是定义private对象来锁定。
让两个线程以相同的加锁顺序加锁,是避免死锁的有效手段。
lock和TryEnter的区别:lock会一直等待锁定对象释放后下一线程才能进入;
TryEnter会返回当前线程是否获取该锁,是则返回true,否则返回flase。
unsafe:需要以指针方式来解决问题的不安全代码能力。
sealed:密封。表示基类不能被继承,或者方法和属性不能被覆写。
sealed不能与abstract共用,因为密封类不能被继承,而抽象类又总是
希望被继承。定义密封方法时,sealed必须和override一起使用。
string类型是密封的;struct是隐式密封的,因此也不能被继承,当一
个类只有静态成员时,可以考虑将其实现为密封类。