类和构造函数
1.当创建一个引用类型的实例时,首先会在内存中分配空间,而后会调用其构造函数对实例进行初始化。元数据中对应构造函数定义:.Ctor.在调用构造函数前,字段都被初始化为0或者null.
2.在定义一个类的时候,默认有一个无参的构造函数存在。也可以定义多个参数不同的构造函数。当调用构造函数时候,它会首先自动调用基类构造函数,直至System.Object的构造函数(一个无参,什么都不做的构造函数)
3.在类中定义一个字段
Code
public class Demo
{
private int i=100;
}
利用Reflector工具可以看到:
Code
public Demo()
{
this.i = 100;
}
编译器会自动在其无参构造函数中对其赋值,也有中叫法,这种方法为“内联代码”(inline)
所以,通常我们定义的字段都要在构造函数中赋值。
Code
public class Demo
{
private int i;
private string s;
static void Main()
{
Demo d = new Demo(20);
Console.WriteLine(d.s);
}
public Demo()
{
this.i = 100;
this.s = "welcome";
}
public Demo(int x)
: this()
{
this.i = x;
}
public Demo(int x, string y)
{
this.i = x;
this.s = y;
}
}
注意,其中利用this关键字,可以在一个构造函数调用另一个构造函数。
结构体和构造函数
1.结构体没有默认的构造函数,所以象上述那样定义字段,是不可行的(找不到构造函数)。
2.在C#中也不能显式的定义无参构造函数。(CLR中允许)
3.一旦为结构体定义构造函数,必须在构造函数中给所有的字段初始化。
Code
struct MyStruct
{
private string tmp;
public int i;
public MyStruct(string s)
{
tmp = s;
//i = 100;//少了任何一句,将报错錯誤:程式控制權離開建構函式前,
//必須指定 'Demo1.MyStruct.i' 欄位的值 D:\Demo\Book_CLRvirCS\Demo1\AccessDemo.cs 57 16 Demo1
}
}
静态构造函数
1.静态构造函数默认Private,并且不能显式定义其访问级别。
2.通常在静态构造函数中初始化静态变量
3.静态构造函数只在类型第一个被调用加载时执行,且只执行一次。
4.对应元数据定义 .cctor
5.如下定义:
Code
public class Demo
{
private static string s = "welcome";
}
对应元数据:
Code
static Demo()
{
s = "welcome";
}
此时也会自动生成一个静态构造函数,并在其中进行赋值。
6.静态构造函数不会调用基类的静态构造函数。
7.通常我们也会这样定义:
Code
internal sealed class Precise
{
public static int s_x;
static Precise()
{
s_x = 123;
}
}
那么这种方式和原来的方式有什么区别呢?(引)
精确语义Precise:针对在cctor中初始化而言();
字段初始化前语义BeforeFieldInit:针对于内联初始化而言。
二者的区别在于是否会在MSIL中的cctor上附加一个BeforeFieldInit标记
性能测试:
Code
namespace Demo1
{
internal sealed class BeforeFieldInit
{
public static int s_x = 123;
}
internal sealed class Precise
{
public static int s_x;
static Precise()
{
s_x = 123;
}
}
public sealed class Program
{
public static void Main()
{
const int iterations = 1000 * 1000 * 1000;
PerfTest1(iterations);
PerfTest2(iterations);
}
private static void PerfTest1(int iterations)
{
Stopwatch sw = Stopwatch.StartNew();
for (int x = 0; x < iterations; x++)
{
BeforeFieldInit.s_x = 1;
}
Console.WriteLine("PerfTest1:{0} BeforeFieldInit",sw.Elapsed);
sw = Stopwatch.StartNew();
for (int x = 0; x < iterations; x++)
{
Precise.s_x = 1;
}
Console.WriteLine("PerfTest1:{0} Precise",sw.Elapsed);
}
private static void PerfTest2(int iterations)
{
Stopwatch sw = Stopwatch.StartNew();
for (int x = 0; x < iterations; x++)
{
BeforeFieldInit.s_x = 1;
}
Console.WriteLine("PerfTest2 :{0} BeforeFieldInit",sw.Elapsed);
sw = Stopwatch.StartNew();
for (int x = 0; x < iterations; x++)
{
Precise.s_x = 1;
}
Console.WriteLine("PerfTest2:{0} Precise",sw.Elapsed);
}
}
}
运行结果:
PerfTest1:00:00:03.2077364 BeforeFieldInit
PerfTest1:00:00:07.6219642 Precise
PerfTest2 :00:00:03.2216907 BeforeFieldInit
PerfTest2:00:00:03.2098247 Precise
Press any key to continue . . .
可见:第一此执行时,内联方式要比精确定义方式要得多,但是之后,就差不多了。
运算符重载
如下定义:
Code
public static OverloadDemo operator +(OverloadDemo o1, OverloadDemo o2)
{
return new OverloadDemo();
}
IL:
Code
.method public hidebysig specialname static class Demo1.OverloadDemo op_Addition(class Demo1.OverloadDemo o1, class Demo1.OverloadDemo o2) cil managed
{
.maxstack 1
.locals init (
[0] class Demo1.OverloadDemo CS$1$0000)
L_0000: nop
L_0001: newobj instance void Demo1.OverloadDemo::.ctor()
L_0006: stloc.0
L_0007: br.s L_0009
L_0009: ldloc.0
L_000a: ret
}
可以看到,IL中对此方法有几个特殊标记:specialname , op_XXX.
当此运算符被调用时,编译器会查找是否有specialname标记的方法存在,如果存在在检查方法参数是否有一个是所在类型别,如果确认正确则调用,否则异常。
应用可见:
http://www.cnblogs.com/Ivan-Yan/archive/2008/06/11/1217436.html
自定义数据类型转换:
http://blog.csdn.net/JustLovePro/archive/2008/09/08/2900548.aspx
通过引用方式给方法传参(out,ref)
关键字告诉CLR,参数以引用的方式传递。CLR对两个关键字的处理是一致的,但是C#编译器则不同:
1.采用out方式传递,传递前不必初始化。但是在方法返回之前必须给其赋值。
2.采用ref方式传递,必须初始化。在方法内部可读可写。
给方法传递可变数目的参数(paras)
1.paras关键字只能位于方法最后一个参数位置。
2.paras关键字告诉编译器参数应用了个定制特性System.ParamArrayAttribute。当调用一个方法时,编译器首先查找不含这个特性的方法中有没有合适的方法可悲调用,如果没有,在检查包含此特性的方法是否适合参数化调用。
3.必须是一个一维数组,可以传null,或者不传。
4.也经常采用方法的重载来实现可以提高性能(参数的传递设计数据的copy,内存分配,比较消耗性能)
声明方法的参数类型(引)
原则1:方法参数尽可能指定为最弱的类型
选用IEnumberable<T>,而不是IList<T>或ICollection<T>
原则2:方法返回类型尽可能指定为最强的类型
返回FileStream,而不是Stream
原则3:如果希望在不影响调用代码的情况下,能对方法内部实现进行修改,这时候返回类型要使用最弱类型中的最强的一个。
使用IList<T> ,而不是List<T>