先说问题描述,现在有一个Test的类,其信息如下:
class Test
{
public string info = "测试类的成员";
}
定义一个人类的类信息:
class Person
{
//声明一个Test的变量引用
Test test = null;
public Person()
{
if (null == test)
{
test = new Test();
Console.WriteLine(Say());
}
}
public virtual string Say()
{
return string.Format("这是人类类的{0}", test.info);
}
}
定义一个Man类,继承了人类类,并且对虚方法进行了重写:
class Man : Person
{
Test test = null;
public Man() : base()
{
if (null == test)
{
test = new Test();
}
Console.WriteLine(Say());
}
public override string Say()
{
return string.Format("这是男人类的{0}", test.info);
}
}
现在在主方法进行如下测试:定义一个Person的对象
static void Main(string[] args)
{
Person p = new Person();
Console.ReadKey();
}
输出结果为:
接下来,初始化一个Man类的对象:
static void Main(string[] args)
{
Man m = new Man();
Console.ReadKey();
}
结果运行起来报错了:
说明对象为空时候进行了调用,下来我们看看报错的信息具体位置
在Man类中的重写Say方法中报错了,说Man类中的test没有被实例化。
这个时候,我想了想无论从哪里看都好着啊,怎么会这样啊?明明在构造函数里面初始化对象了?怎么会是空呢?
单步调试,设置断点,调试中发现,Man类继承Person类,在Main方法初始化Man类对象时候,根据继承规则,首先调用父类的构造函数,执行父类构造函数初始化test变量,并且调用Say这个虚方法,但是当执行到调用虚方法时候直接跳到子类Man中执行进行重写的Say方法,这时候,子类的test变量还没被初始化,因为父类构造函数没有执行完,子类还没有调用到构造函数,所以没有实例化子类的test成员,进而使用test.info时候,运行时报错。
这里就是一个陷阱,解决办法只有一个,说了感觉跟没说一个样,就是不再在子类的构造函中间接或者直接的调用虚方法。