注:书本第8章:方法
实例构造器和类(引用类型)
构造器方法在“方法定义元数据表”中始终叫做.ctor(constructor的简称)。
构造引用类型的对象,在调用类型的实例构造器之前,为对象分配的内存总是先被归零。
如果类没有显示定义任何构造器,c#编译器将定义一个默认(无参)构造器。如果类的修饰符为abstrat,那么编译器生成的默认构造器的可访问性就为protected;否则,构造器会被赋予public可访问性。静态类编译器根本不会生成默认构造器。
类的实例构造器在访问从基类继承的任何字段之前,必须先调用基类的构造器。
Obect的MemberwiseClone方法可以在不调用实例构造器的前提下创建类型的实例。
“内联”初始化,编译器会转换成调用构造器方法中的代码来执行。这样会容易使代码膨胀。
实例构造器和结构(值类型)
值类型其实并不需要定义构造器,c#编译器根本不会为值类型内联(嵌入)默认的无参构造器。
考虑到性能,CLR不会为包含在引用类型中的每个值类型字段都主动调用构造器。但是,值类型的字段会被初始化为0或null。
CLR确实允许为值类型定义构造器,但是必须显示调用才会执行。如下例子:
internal struct Point{
public Int32 m_x,m_y;
public Point(Int32 x,Int32 y){
m_x=x;
m_y=y;
}
}
internal sealed class Rectangle{
public Point m_topLeft,m_bottomRight;
public Rectangle(){
//在C#中,向一值类型应用关键字new,
//可以调用构造器来初始化值类型的字段
m_topLeft=new Point(1,2);
m_bottomRight=new Point(100,200);
}
}
C#编译器不允许值类型定义无参构造器;但是CLR允许。所以C#不能在值类型中内联初始化字段。但是可以为静态字段进行内联初始化。
在值类型的构造器中,this代表值类型本身的一个实例,用new创建的值类型的一个实例可以赋给this。而在引用类型的构造器中,this被认为是只读的,所以不能对它进行赋值。
类型构造器(静态构造器、类构造器)
类型构造器方法总是叫.cctor。
类型默认没有定义类型构造器。如果定义,也只能定义一个。此外,类型构造器永远没有参数。c#自动将类构造器标记为private。不允许出现修饰符。
类型构造器只会被执行一次。
值类型可以定义类型构造器(但如前所述,不能定义无参实例构造器),但永远不会被执行。
类型只有在AppDomain卸载时才会卸载。AppDomain卸载时,用于标识类型的对象(类型对象)将成为“不可达”的对象(不存在对它的引用),垃圾回收器会回收类型对象的内存。
CLR不支持静态Finalize方法。可以在System.AppDomain类型的DomainUnload事件登记一个回调方法。
操作符重载方法
CLR规范要求操作符重载方法必须是public和static方法。要求操作符重载方法至少有一个参数的类型与当前定义这个方法的类型相同,以便C#编译器能在合理的时间内找到要绑定的操作符方法。
public sealed class Complex{
public static Complex operator+(Complex c1,Complex c2){
}
}
编译器为名为op_Addition的方法生成元数据方法定义项;这个方法还设置了specialname标志,表明这是一个“特殊”方法。
转换操作符方法
将对象从一个类型转换为另一种类型的方法。CLR规范要求转换操作符重载方法必须是public和static方法。
一般的非基元类型,在执行强行转型时,CLR只是检查对象的类型和目标类型(或者从目标类型派生的其他类型)是不是相同。
public sealed class Rational{
//由一个Int32构造一个Rational
public Rational (Int32 num){ ... }
//由一个Single构造一个Rational
public Rational(Single num){ ... }
//将一个Rational转换成一个Int32
public Single ToString(){ ... }
//由一个Int32隐式构造并返回一个Rational
public static implicit operator Rational(Int32 num){
return new Rational(num);
}
//由一个Single隐式构造并返回一个Rational
public static implicit operator Rational(Single num){
return new Rational(num);
}
//由一个Rational显式返回一个Int32
public static explicit operator Int32(Rational r){
return r.ToInt32();
}
//由一个Rational显示返回一个Single
public static explicit operator Single(Rational r){
return r.ToSingle();
}
}
在C#中,impicit关键字告诉编译器为了生成代码来调用方法,不需要在源代码中进行显示转型,expicit只有发现了显示转型,才调用方法。
只有在转换不损失精度或数据级的前提下(比如将个Int32转换成Rational)才能定义隐式转换操作符。如果有,应该定义一个显式转换操作符。
使用强制类型转换表达式时,C#生成代码调用显式转换操作符方法。使用as或is操作符时,则永远不会调用这些方法。
扩展方法
它允许定义一个静态方式,并用实例方法的语法来调用。要将方法变成扩展方法,只需在第一个参数前加this关键字:
public static class StringBuilderExtensions{
public static Int32 IndexOf(this StringBuilder sb,Char value){//假定StringBuilder中没有IndexOf方法
for(Int32 index=0;index<sb.Length;index++)
if(sb[index]==value)return index;
return -1;
}
}
Int32 index=sb.IndexOf('X');
C#只支持扩展方法,不支持扩展事件、属性、操作符等。
分部方法
//工具生成的代码,存储在某个源代码文件中
internal sealed partial class Base{
private String m_name;
//这是分部方法的声明
partial void OnNameChanging(string value);
public String Name{
get{ return m_name; }
set{
OnNameChanging(value.ToUpper());//通知类要进行更改了
m_name=value;//更改字段
}
}
}
//开发人员生成的代码,存储在另一个源代码文件中:
internal sealed partial class Base{
//这是分部方法的实现,会在m_name更改前调用
partial void OnNameChanging(String value){
if(String.IsNullOrEmpty(value))
throw new ArgumentNullException("value");
}
}
分部方法只能在分部类或结构中声明;
分部方法的返回类型始终是void,任何参数都不能用out修饰符来标记。
分部方法总是被视为private方法,但是c#编译器禁止在分部方法声明之前添加private关键字。