引用类型构造器
如果我们没有定义实例构造器,那么编译器会为我们默认产生一个无参构造器。
实例对象初始化过程
- 为实例分配内存;
- 初始化附加成员,包括方法表指针和SyncBlockIndex变量(我们已经在
调用顺序
如果类没有显示定义构造器,编译器会自动生成一个无参构造器,调用基类的无参构造器。例如
public class Animal{}
相当于
public class Animal
{
public Animal():base(){}
}
如果类的修饰符为static(sealed和abstract),编译器不会默认生成构造器;
如果基类没有提供无参构造器,那么派生类必须显示调用一个构造器,否则编译错误。
如果存在继承关系,派生类在使用基类的字段之前应该先调用基类的构造器。如果派生类没有显式调用基类构造器,则编译器会自动产生调用基类无参构造器的代码,沿着继承层次一直到System.Object的无参构造器位置。例如下面,调用Dog dog=new Dog()方法的结果。
class Dog:Animal。。。
Dog()方法IL代码
代码爆炸?
为了防止构造器重载时大量重复赋值造成代码膨胀,我们建议将公共的初始化语句放在一个构造函数中,然后其他的构造器显式调用该构造器。
class A
{
public int xxxx = ;
public int xxxx2 = ;
public int xxxx3 = ; public A()
{
Console.WriteLine("我是类A的无参构造器");
} public A(string ss)
{
Console.WriteLine("我是类A的无参构造器");
}
} //编译后等价于
class A
{
public int xxxx;
public int xxxx2;
public int xxxx3; public A()
{
xxxx = ;
xxxx2 = ;
xxxx3 = ;
Console.WriteLine("我是类A的无参构造器");
} public A(string ss)
{
xxxx = ;
xxxx2 = ;
xxxx3 = ;
Console.WriteLine("我是类A的无参构造器");
}
} /// <summary>
/// ////////////////////////////////////////////////////////////////////////////////////////////////
/// </summary>
class A
{
public int xxxx = ;
public int xxxx2 = ;
public int xxxx3 = ; public A()
{
Console.WriteLine("我是类A的无参构造器");
} //防止代码爆炸
public A(string ss):this()
{
Console.WriteLine("我是类A的无参构造器");
}
} //编译后等价于
class A
{
public int xxxx;
public int xxxx2;
public int xxxx3; public A()
{
xxxx = ;
xxxx2 = ;
xxxx3 = ;
Console.WriteLine("我是类A的无参构造器");
} public A(string ss):this()
{
Console.WriteLine("我是类A的无参构造器");
}
}
值类型构造器
- 值类型没有默认产生的无参构造器,也不允许我们定义无参构造器。但是我们可以自定义带参数的构造器。
- 不允许在值类型中内联实例字段的初始化。下面的例子会产生编译错误。
struct TestStruct
{
partial int number=5;
}
- 值类型带参构造函数必须对所有实例字段进行初始化才可以。如果有变量没有初始化,就会报错。
如果不想对所有字段一一初始化,有一种替代方案:
struct Dog
{
public int age;
public string name;
public Dog(string Name)
{
this = new Dog();
name = Name;
}
}
{
public int age;
public string name;
public Dog(string Name)
{
this = new Dog();
name = Name;
}
}
在值类型构造器中,this代表值类型本身的一个实例,用New创建的值类型实例赋给this时,会将所有字段置零。所以这个方案可以编译通过。
- 带参构造函数定义之后需要用new显式调用才能执行。否则值类型的字段会保持0或Null值。