目录
静态构造函数
我们在使用类中的静态成员时,先要初始化这些成员。 在声明静态成员时、可以同时提供一个初始值。 但有时候我们需要执行更复杂的初始化操作, 这应该怎么办呢?
我们可以把构造函数声明成static的, 一般来说, 静态的构造函数初始化类的静态字段。
关于静态构造函数应注意的有:
- 那么一个类只能有一个静态构造函数,而且不能带参数、没有返回值。 也不能有访问修饰符(比如private)。 而且声明时使用static 关键字作为前辍。
- 那么一个类既可以有静态构造函数也可以有实例构造函数。
- 跟静态成员函数类似, 静态构造函数不能访问所在类的实例成员, 所以也不能使用this访问器。
不能在程序中显式调用静态构造函数,系统会自动调用它们,在:
- 只要一个类有静态构造函数,当我们创建第一个类实例之前,都会调用静态构造函数、
- 只要一个类有静态构造函数,在引用该类的任何静态成员之前
在这两种情况下,会首先调用静态构造函数, 之后实例化类或访问静态成员。应注意的是: 其实我们创建了多个个类实例, 其静态构造函数都只会调用一次。
下面看一个程序代码:
namespace Ch05Ex03
{
class RandomNum
{
static int RandomKey;
int aa;
int bb;
static RandomNum()
{
aa = 17;//错误,静态构造函数不可以访问非静态字段
bb = 18; //错误,静态构造函数不可以访问非静态字段
RandomKey = 15;
WriteLine("静态构造函数被调用!");
}
public RandomNum(int _aa,int _bb)
{
aa = _aa;
bb = _bb;
WriteLine("\n实例构造函数被调用!");
}
public void Show()
{
WriteLine($"输出RandomKey的静态成员的值:{RandomKey}");
WriteLine($"输出aa的静态成员的值:{aa}");
WriteLine($"输出bb的静态成员的值:{bb}");
WriteLine();
}
}
class Program
{
static void Main(string[] args)
{
RandomNum a = new RandomNum(2,3);
RandomNum b = new RandomNum(5,6);
a.Show();
b.Show();
ReadKey();
}
}
}
输出是结果为:
静态构造函数被调用!
实例构造函数被调用!
实例构造函数被调用!
输出RandomKey的静态成员的值:15
输出aa的值:2
输出bb的的值:3
输出RandomKey的静态成员的值:15
输出aa的值:5
输出bb的的值:6
在该程序中, 我们首先创建了该类的两个实例,分别为它填了两个实参, 我可以看到输出结果,不管创建多少个实例, 静态构造函数只调用一次,而且是最先被调用的那一个; 其次就是调用创建该实例的对应的实例构造函数, 上面的程序调用的是带两个形参的实例构造函数。 调用顺序为你创建实例的顺序。
那么每一个实例都有一个静态成员的值,它被所有类实例所共享。当你一个实例改变了静态字段的值,那么其它的实例的静态字段的值也将被改变。 因为类的所有实例都共享一块静态字段的内存空间。
对象初始化语句
namespace Ch05Ex03
{
class RandomNum
{
public int aa;
public int bb;
public RandomNum()
{
aa = 55;
bb = 66;
WriteLine("默认的构造函数被调用!");
}
public RandomNum(int _aa,int _bb)
{
aa = _aa;
bb = _bb;
WriteLine("\n实例构造函数被调用!\n");
}
}
class Program
{
static void Main(string[] args)
{
RandomNum pt1 = new RandomNum(); //调用默认的构造函数
//使用对象初始化语句初始化该类的公有字段
RandomNum pt2 = new RandomNum { aa = 7, bb = 8 }; //调用默认构造函数
RandomNum pt3 = new RandomNum (2,3){ aa = 5, bb = 6 }; // 调用带参的实例构造函数
WriteLine($"输出pt1中的值:{pt1.aa} , {pt1.bb}");
WriteLine($"输出pt2中的值:{pt2.aa} , {pt2.bb}");
WriteLine($"输出pt3中的值:{pt3.aa} , {pt3.bb}");
ReadKey();
}
}
}
输出结果为:
默认的构造函数被调用!
默认的构造函数被调用!
实例构造函数被调用!
输出pt1中的值:55 , 66
输出pt2中的值:7 , 8
输出pt3中的值:5 , 6
如果一个类如果有公有字段时, 我们即可以在构造函数中初始化, 我们也可以在类外定义类实例时,用该实例对象一一初始化公有字段。类似于这样:
pt1.aa = 100;
pt1.bb = 200;
其实还有还有一种方式是: 使用对象初始化语句来初始化该类的公有字段。 这样,我们就可以在创建对象的实例时,使用此方法可以 设置 public 字段和属性的值。
该语句有两种形式:
类名 实例名 = new 类名 {字段名称 = 初始值};
类名 实例名 = new 类名(实参表) {字段名称 = 初始值};
注意: 使用这样形式初始化字段和属性, 该字段和属性必须是public的。
注意: 初始化发生在构造方法执行之后。 所以在构造函数中设置的实参值会在对象初始化中重置为相同或不同的值。在上述程序该代码中:
RandomNum pt3 = new RandomNum (2,3){ aa = 5, bb = 6 }; // 调用带参的实例构造函数
我们在构造函数中的实参表设置值2、3, 又在 { } 中设置了值5、6, 那么首先调用带参的构造函数初始化,之后又被 { } 该列表中设置的值初始化,所以 输出的值是 5、6, 而不是2、3.
注意:
RandomNum pt2 = new RandomNum { aa = 7, bb = 8 }; //调用默认构造函数
该代码调用的是默认的构造函数, 因为他没有提供实参表。 注意: 用该方法初始化类的公有字段时,必须要有默认的构造函数,不然错误。
下面看一个示例代码,看对象初始化语句如何设置 属性的值:
namespace Ch05Ex03
{
class RandomNum
{
int aa; // 定义私有字段
public RandomNum()
{
aa = 55;
WriteLine("默认的构造函数被调用!");
}
public RandomNum(int _aa)
{
aa = _aa;
WriteLine("\n实例构造函数被调用!\n");
}
public void Show()
{
WriteLine($"\n输出aa的值:{aa}\n");
}
public int MyValue // 定义公有属性
{
set
{
if( value > 0)
{
aa = value;
}
}
get
{
return aa;
}
}
}
class Program
{
static void Main(string[] args)
{
RandomNum myRandomNum = new RandomNum();
myRandomNum.Show();
RandomNum myRandomNum11 = new RandomNum(60);
myRandomNum11.Show();
//调用默认的构造函数, 必须要有默认构造函数,不然错误
RandomNum myRandomNum12 = new RandomNum { MyValue = 600}; //用对象初始化语句设置公有属性的值,如果说属性是私有的,就不可以
myRandomNum12.Show();
WriteLine($"直接输出属性设置的值:{myRandomNum12.MyValue}");
RandomNum myRandomNum13 = new RandomNum(70) { MyValue = 1000 }; //调用带参的实例构造函数
myRandomNum13.Show();
WriteLine($"直接输出属性设置的值:{myRandomNum13.MyValue}");
ReadKey();
}
}
}
输出结果为:
默认的构造函数被调用!
输出aa的值:55
实例构造函数被调用!
输出aa的值:60
默认的构造函数被调用!
输出aa的值:600
直接输出属性设置的值:600
实例构造函数被调用!
输出aa的值:1000
直接输出属性设置的值:1000