目录
8.1 实例构造器和类(引用类型)
8.2 实例构造器和结构(值类型)
8.3 类型构造器
8.4 操作符重载方法
8.5 转换操作符方法
8.6 扩展方法
8.7 分布方法
8.1 实例构造器和类(引用类型)
构造器是将类型的实例初始化为良好状态的特殊方法。构造器方法在“方法定义元数据表”中始终叫做.ctor。创建引用类型的实例时,首先为实例的数据字段分配内存,然后初始化对象的附加字段(类型对象指针和同步块索引)。最后调用类型的实例构造器来设置对象的初始状态。构造引用类型的对象时,在调用类型的实例构造器之前,为对象分配的内存总是先被归零。没有被构造器显示重写的所有字段都保证获得0或null值。
实例构造器永远不能被继承,所以实例构造器不能使用以下字符修饰符:virtual,new,override,sealed,abstract。如果没有显示定义任何构造器,C#编译器将定义一个默认(无参)构造器。在它的实现中,只是简单地调用了基类的无参构造器。
8.2 实例构造器和结构(值类型)
值类型构造器的工作方式与引用类型的构造器截然不同。CLR总是允许创建值类型的实例,并且没有办法阻止值类型的实例化。所以,值类型其实并不需要定义构造器,C#编译器根本不会为值类型内联(嵌入)默认的无参构造器。
8.3 类型构造器
类型构造器也称为静态构造器,类构造器或者类型初始化器。可用于接口(C#编译器不允许),引用类型和值类型。类型构造的作用时设置类型的初始化状态。类型默认没有定义类型构造器。如果定义,也只能定义一个。此外,类型构造器永远没有参数。
当方法被JIT编译完毕之后,线程开始执行它,最终会执行到调用类型构造器的代码。多个线程可能同时执行相同的方法。CLR希望确保在每个AppDomain中,一个类型构造器只能执行一次。为了保证这点,在调用类型构造器时,调用线程要获取一个互斥线程同步锁。这样一来,如果多个线程试图同时调用某个类型的静态构造器,只有一个线程才可以获得锁,其他线程会被阻塞。第一个线程会执行静态构造器中的代码。当第一个线程离开构造器后,正在等待的线程被唤醒,然后发现构造器的代码已被执行过,因此,这些线程不会再次执行代码,将直接从构造器方法返回。初次之外,如果再次调用这样的一个方法,CLR知道类型构造已被执行过,从而确保构造器不被再次调用。
类型构造器中的代码只能访问类型的静态字段,并且它的常规用途就是初始化这些字段。
类型构造器不应调用基类型的类型构造器,因为类型不可能有静态字段是从基类型分享或继承的。
8.4 操作符重载方法
CLR规范要求操作符重载方法必须是public和static方法。C#要求操作符重载方法至少有一个参数的类型与当前定义这个方法的类型相同
C#的一元操作符及其相容于CLS的方法名
C#操作符 | 特殊方法名 | 推荐的相容于CLS的方法名 |
+ | op_UnaryPlus | Plus |
- | op_UnaryNegation | Negate |
! | op_LogicalNot | Not |
~ | op_OnesCompement | OnesComplement |
++ | op_Increment | Increment |
-- | op_Decrement | Decrement |
(无) | op_True | IsTrue{get;} |
(无) | op_False | IsFalse{get;} |
C#的二元操作符及其相容于CLS的方法名
C#操作符 | 特殊方法名 | 推荐的相容于CLS的方法名 |
+ | op_Addition | Add |
- | op_Subtraction | Subtract |
* | op_Multiply | Multiply |
/ | op_Divsion | Divide |
% | op_Modulus | Mod |
& | op_BitwiseAnd | BitwiseAnd |
| | op_BitwiseOr | BitwiseOr |
^ | op_ExclusiveOr | Xor |
<< | op_LeftShift | LeftShift |
>> | op_RightShift | RightShift |
== | op_Equality | Equls |
!= | op_Inequality | UnEquls |
< | op_LessThan | Compare |
> | op_GreaterThan | Compare |
<= | op_LessThanOrEqual | Compare |
>= | op_GreaterThanOrEqual | Compare |
编译器会为特殊操作符对应的方法生成元数据方法定义项,这个方法定义项还设置了specialname标识,表明这是一个特殊方法。编程语言的编译器看到代码中出现操作符时,会检查是否有一个操作数的类型定义了对应特殊操作符的specialname方法,而且该方法的参数兼容于操作数的类型。如果存在这样的方法,编译器就生成调用它的代码。不存在这样的方法就报错。
8.5 转换操作符方法
转换操组符是将对象从一种类型转换成另一种类型的方法。可以使用特殊的语法来定义转换操作符方法。CLR规定要求转换操作符重载方法必须是public和static方法。C#要求参数类型和返回类型二者必有其一与定义转换方法的类型相同。
8.6 扩展方法
8.6.1 规则和原则
C#只支持扩展方法,不支持扩展属性,扩展事件,扩展操作符等。
扩展方法必须在非泛型的静态类中声明。扩展方法至少要有一个参数,而且只有第一个参数能用this关键字标记。
C#编译器在静态类中查找扩展方法时,要求静态类本身必须具有文件作用域。不能嵌套另一个类中。
由于静态类可以取任何名字,所以C#编译器要花一定时间来寻找扩展方法,它必须检查文件作用域中的所有静态类,并扫描它们的所有静态方法来查找一个匹配。
多个静态类可以定义相同的扩展方法。需要使用静态方法语法调用扩展方法。
扩展方法需要谨慎使用。扩展方法可能存在版本控制问题。
8.6.2 用扩展方法扩展各种类型
扩展方法实际是对一个静态方法的调用,所以CLR不会生成代码对调用方法的表达式的值进行null值检查。
可以为接口类型,委托类型和枚举类型定义扩展方法
8.6.3 ExtensionAtrribute类
在C#中,一旦用this关键字标记了某个静态方法的第一个参数,编译器就会在内部向该方法应用ExtensionAtrribute特性。该特性会在最终生成的文件的元数据中持久性地存储下来。
任何静态类只要包含至少一个符合上述特点的静态类,在它的元数据中也会应用这个特性。类似的,任何程序集只要包含至少一个符合上述特点的静态类,它的元数据也会应用这个特性。这样一来,如果代码调用了一个不存在的实例方法,编译器就能快速扫描引用的所有程序集,判断它们哪些包含了扩展方法。然后,在这些程序集中,可以只扫描包含了扩展方法的静态类。在每个这样的静态类中,可以只扫描扩展方法来查找匹配。
8.7 分部方法
规则和原则
它们只能在分部类或结构中声明。
分部方法的返回类型始终是void,任何参数都不能用out修饰符来标记。之所以有这两个限制,时因为方法在运实时可能不存在,所以不能讲变量初始化为方法也许会返回的东西。不允许out参数也是因为方法必须初始化它,而方法可能不存在。分部方法可以有ref参数,可以是泛型方法,可以是实例或静态方法,而且可标记为unsafe。
分部方法的声明和实现必须具有一致的签名。如果两者都应用了定制特性,编译器会合并两个方法的特性。应用于参数的任何特性也会合并。
如果没有对应的实现部分,便不能再代码中创建一个委托来引用这个分部方法,这同样是由于方法在运行时不存在。
分部方法总是被视为private方法,但C#编译器禁止在分部方法声明之前添加private关键字