对构造函数的描述 II
初始华数据
使用初始化列表:
使用特殊的语法允许一个构造函数调用另一个构造函数重载。
目的:可以用来避免重复的初始化代码。如下面的构造函数:
class Date
{
public Date( )
{
ccyy = 1970;
mm = 1;
dd = 1;
}
public Date(int year, int month, int day)
{
ccyy = year;
mm = month;
dd = day;
}
private int ccyy, mm, dd;
}其中的ccyy,mm,dd赋值重复。这样做的缺点是:如过要改变ccyy,mm,dd的值,需要更改2处。
避免以上问题的常规方法是:
class Date
{
public Date( )
{
Init(1970, 1, 1);
}
public Date(int year, int month, int day)
{
Init(day, month, year);
}
private void Init(int year, int month, int day)
{
ccyy = year;
mm = month;
dd = day;
}
private int ccyy, mm, dd;
}使用初始化列表。
初始化列表允许在同一个类中,一个构造函数调用另一个构造重载。
class Date
{
public Date( ) : this(1970, 1, 1)
{
}
public Date(int year, int month, int day)
{
ccyy = year;
mm = month;
dd = day;
}
private int ccyy, mm, dd;
}1.不能使用非构造函数使用初始化列表;
class Point
{
public Point(int x, int y) {
}
// Compile-time error
public void Init( ) : this(0, 0) { }
}2.不允许构造函数使用初始列表调用自身;
class Point
{
// Compile-time error
public Point(int x, int y) : this(x, y) { }
}
class Point
{
// Compile-time error
public Point( ) : this(X(this), Y(this)) { }
public Point(int x, int y) {
}
private static int X(Point p) {
}
private static int Y(Point p) {
}
}声明只读变量和常数:
可以声明只读变量:
readonly int nLoopCount = 10;常量:指它的数值不发生变化;
const int speedLimit = 55;
const int speedUnLimit = speedLimit*2;初始化只读字段:
不允许重复赋值的字段可以定义为只读字段。
初始化只读字段有3种方法:
1.使用只读字段的默认值;
系统默认的构造函数会初始化所有的字段,不论字段是否是只读。如果自己定义了构造函数,但是不显示的初始化各个字段,编译器也会初始化各个字段。
class SourceFile
{
public SourceFile( ) { }
public readonly ArrayList lines;
}
class Test
{
static void Main( )
{
SourceFile src = new SourceFile( );
Console.WriteLine(src.lines == null); // Still true
}
}
2.在构造函数中初始化;
class SourceFile
{
public SourceFile( )
{
lines = new ArrayList( );
}
private readonly ArrayList lines;
}这样做的好处是,可以使用构造函数的参数初始化只读字段:
class SourceFile
{
public SourceFile(int suggestedSize )
{
lines = new ArrayList(suggestedSize);
}
private readonly ArrayList lines;
}
3.在声明只读字段时定义;
class SourceFile
{
public SourceFile( )
{

}
private readonly ArrayList lines = new ArrayList( );
}上面的声明,编译器编译时,还会把初始化部分放在构造函数中。
给结构声明构造函数:
结构的构造函数语法同类的构造函数语法相同:
struct Point
{
public Point(int x, int y) {
}

}声明结构的构造函数的限制:
1.编译器会自动建立一个默认的构造函数;
2.你不能够声明默认的构造函数;
3.不能生命Protected的构造函数;
4.必须初始化所有字段;
编译器会自动建立一个默认的构造函数:
不论你是否已经建了1个构造函数,编译器会自动生成一个默认的构造函数。这点与类不同,在类中,如果建立了自己的构造函数,编译器是不会再增加构造函数的。
在结构的默认的构造函数中,所有字段被初始化为0,false,null.
struct SPoint
{
public SPoint(int x, int y) {
}

static void Main( )
{
// Okay
SPoint p = new SPoint( );
}
}
class CPoint
{
public CPoint(int x, int y) {
}

static void Main( )
{
// Compile-time error
CPoint p = new CPoint( );
}
}说明,其中的SPoint p=new SPoint(),在堆栈上建立了新的Struct并初始化了所有字段,这点不可改变。
代码SPoint p;只是在堆栈上建立struct ,并不初始化字段。
struct SPoint
{
public int x, y;

static void Main( )
{
SPoint p1;
Console.WriteLine(p1.x); // Compile-time error
SPoint p2;
p2.x = 0;
Console.WriteLine(p2.x); // Okay
}
}原因是:编译器会自动建立这个默认的构造函数,如过再次建立,将会出现代码重复。
class CPoint
{
// Okay because CPoint is a class
public CPoint( ) {
}

}
struct SPoint
{
// Compile-time error because SPoint is a struct
public SPoint( ) {
}

}我们可以建立带有参数的构造函数,这时,我们需要给各个字段手工赋值。
不允许在结构中建立Protected的构造函数:
原因:结构不允许继承,所以protected就没有意义了。
class CPoint
{
// Okay
protected CPoint(int x, int y) {
}
}
struct SPoint
{
// Compile-time error
protected SPoint(int x, int y) { .. . }
}所有字段必须赋初始值:
在类中,如果在构造函数中不给字段赋值,那么编译器可以保证所有字段还有默认值。
class CPoint
{
private int x, y;
public CPoint(int x, int y) { /*nothing*/ }
// Okay. Compiler ensures that x and y are initialized to
// zero.
}但是在结构中,如果这样做,会发生编译错误:
struct SPoint1 // Okay: initialized when declared
{
private int x = 0, y = 0;
public SPoint1(int x, int y) { }
}
struct SPoint2 // Okay: initialized in constructor
{
private int x, y;
public SPoint2(int x, int y)
{
this.x = x;
this.y = y;
}
}
struct SPoint3 // Compile-time error
{
private int x, y;
public SPoint3(int x, int y) { }
}在数学函数中,使用静态和实例方法的对照:
使用实例:
class Cumbersome
{
static void Main( )
{
Math m = new Math( );
double answer;
answer = m.Cos(42.0);
// Or
answer = new Math( ).Cos(42.0);
}
}静态方法:
class Math
{
public static double Cos(double x) {
}
public static double Sin(double x) {
}
}
class LessCumbersome
{
static void Main( )
{
double answer = Math.Cos(42.0);
}
}静态方法更加简洁些,因为不需要建立实例,速度更快些。
但是还存在一个问题,编译器会建立默认构造函数,是Public的,但是我们只需要使用静态的方法,从不使用这个默认的构造函数。
存在2个方法解决:
1.把Math声明为Abstract的类;
2.建立Private的构造函数;通过建立Private的构造函数,可以阻止编译器建立构造函数,因为是Private的,可以阻止Math建立实例,同样可以阻止使Math成为基类。
使用静态的构造函数:
构造函数的实例确保对象在使用前的状态是正常的,而静态的构造函数可以确保类在使用前的状态是正常的。
在run-time加载类
C# 是动态语言,当CLR运行.Net程序时,经常遇到没有加载到内存的类,这时,执行将暂停,动态加载所需类,然后继续执行。
C#确保类在使用前被初始化,这个由静态构造函数完成。
1.类的静态构造函数在类的任何实例建立前执行;
2.静态构造函数在任何静态成员建立饮用前执行;
3.静态构造函数在任何其它衍生类的静态构造函数执行前执行;
4.静态构造函数只执行一次;
静态构造函数的最常用的用途是初始化静态字段。
使用静态构造函数的限制:
1.不允许调用静态构造函数;
2.不允许使用修饰符来建立静态构造函数;
3.不允许静态构造函数带任何参数;
4.不允许在静态构造函数中使用this关键字;
参考:MSDN Training
Introduction to C# Programming
for the Microsoft® .NET Platform
(Prerelease)
Workbook
Course Number: 2124A


浙公网安备 33010602011771号