[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);
}

结果如下:

[C#1] 6-方法

转换操作符方法必须为public和static.上述四个静态方法会被编译成如下代码:

[C#1] 6-方法

方法明总是为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则显得比较"光明正大"...

作者:Blackheart
上一篇:超详细 Nginx 极简教程,傻瓜一看也会!


下一篇:[C#1] 9-委托