[C#1] 6-方法
1.实例构造器[.ctor]
默认情况下,对于引用类型,如果我们没有显示的定义实例构造器,则C#编译器会为我们定义一个无参的公有实例构造器。 一个类的实例构造器在访问基类的继承字段之前,必须调用基类的实例构造器,C#编译器会自动产生对基类默认构造器的调用代码。
特殊情况下类型实例的创建不会调用实例构造器:反序列化一个对象时、调用Object的MemberwiseClone方法克隆对象时。
C#值类型不允许定义无参的构造器,CLR允许这么做
2.类型构造器[.cctor]
类型构造器又称静态构造器。C#只允许一个静态构造器,不允许有访问修饰符[默认private]不能有参数。 静态构造器由CLR负责,一旦被调用,那么在整个应用程 序域[AppDomain]的生命周期内就不再被用;静态构造器不应该调用基类的静 态构造器,不需要这样做是因为基类的静态成员并不被派生类所继承。但是我们看 到的是派生类内部引用可基类的静态字段,事实上这不是继承而是编译时静态绑定, 其他的静态成员也是如此的。
3.操作符重载方法[operator]
CLR对操作符一无所知,它就不认识什么是操作符。但是却规范了编程语言应 该怎么重载操作符,对CLR而言,重载操作符仅仅是一些方法。如下代码:
class MyType { public static MyType operator +(MyType mt1, MyType mt2) { //.... } }
编译器会产生一个名为op_Addition的方法,该方法上有一个specialname标记,表 示这是一个特殊的方法。当编译器看到源代码中的“+”时,就会去看其中的操作数据 类型中有哪一个定义了参数类型和操作类型兼容的、名为op_Addition的specialname 方法。如果存在就产生调用该方法的代码,如果不存在就出现编译错误了。
一些核心 的FCL类型并没有定义任何操作符重载方法(Decimal除外),因为 CLR直接提供了IL 指令支持直接操作这些类型。可以避免些性能的损失,因为如果提供了方法,最终还是 调用的IL指令,所以FCL的核心类型(如 int,byte...)就省去了这些操作符重载方法
4.转换操作符方法[implicit、explicit]
有些时候,我们需要将一个类型的对象转化为另一个类型的对象。当源类型和 目标类型都是编译器认识的基元类型时,编译器将知道产生必要的代码来执行这样 的转化[如Byte转为Int32]。但是当有一个类型不是编译器认为的基元类型时 [MyType转为Int32],编译器将不知道怎样执行转化。为了试这些转化成为可能,须写如下代码:
1 class MyType 2 { 3 private double _value; 4 public MyType() { } 5 public MyType(int value) 6 { 7 this._value = value; 8 } 9 public MyType(double value) 10 { 11 this._value = value; 12 } 13 public int ToInt() 14 { 15 return checked((int)this._value); 16 } 17 public double ToDouble() 18 { 19 return this._value; 20 } 21 //由int隐式转换为MyType 22 public static implicit operator MyType(int value) 23 { 24 return new MyType(value); 25 } 26 //由double隐式转换为MyType 27 public static implicit operator MyType(double value) 28 { 29 return new MyType(value); 30 } 31 //由MyType显示返回一个int 32 public static explicit operator int(MyType myType) 33 { 34 return myType.ToInt(); 35 } 36 //由MyType显示返回一个double 37 public static explicit operator double(MyType myType) 38 { 39 return myType.ToDouble(); 40 } 41 public override string ToString() 42 { 43 return this._value.ToString(); 44 } 45 }
测试代码:
static void Main(string[] args) { MyType mt1 = 100;//int隐式转为MyType MyType mt2 = 100.06d;//double隐式转为MyType Console.WriteLine(mt1); Console.WriteLine(mt2); int imt1 = (int)mt1;//将MyType显式转为int double imt2 = (double)mt2;//将MyType显式转为double Console.WriteLine(imt1); Console.WriteLine(imt2); }
结果如下:
转换操作符方法必须为public和static.上述四个静态方法会被编译成如下代码:
方法明总是为op_Implicit和op_Explicit.但是我们发现前两个方法签名除了返回类型不同之外其他完全相同。 这是因为CLR支持一个类型定义多个只有返回值类型不同的方法,然而很少有语言可以提供如此的能力,C#就不支持这样做。
转换学习模 版[System.Decimal类]。
5.引用参数
默认情况下。CLR对所有的方法参数都是按值传递的[值类型传值的副本,引< 用类型传引用的副本]。CLR当然也支持按引用的方式传递参数,C#中用out和ref 关键字来支持。这两个关键字告诉编译器产生额外的元数据来表示指定的参数是按 引用的方式传递的[参数的地址,而不是参数本身的值]。
从IL或者CLR的角度,out和ref实际是一样的。两者的不同是编译器会选择不同 的机制来检测我们代码。out修饰的参数在调用前可以不初始化,并且被调方法不能
接读取该参数的值,必须在方法返回之前为参数赋值。ref修饰的参数调用前则必须初 始化。
可以根据out和ref参数进行方法重载,但是它们两个之间[也就是只区分out和ref]不能构成重载。
6.可变数目参数:[params]
声明例子:
//关键字params[System.ParamArrayAttribute的简写] //完整写法:[System.ParamArrayAttribute] string[] items public static void Show(int num, params string[] items) { //... }
当编译器检测到方法调用时,会根据指定的方法名检查所有不含ParamArrayAttribute的方法,如果符合条件,调用该方法。如果没有找到符合条件的, 就会查找有ParamArrayAttribute特性的方法看其是否满足调用需求。 如果满足,首先会产生一系列指令来构造数据以及用指定的元素填充数据,完成上述操作后才调用该方法。
注意只有方法的最后一个参数才可以用params修饰。
7.虚方法
virtual关键字修饰的方法称为虚方法,此方法允许派生类型重写该方法。虚方法的重写[override、new]:new表示子类重写了父类的方法,但是它隐藏掉了重写的这一事实[就像是子类重新引入的方法一样,和父类没有任何关系了。显得比较"猥琐",重写了还不要让人知道]。override则显得比较"光明正大"...