先来看一个gif:
什么情况?明明已经赋值把obj.m_pA赋值为null了,可是为什么单步调试后m_pA却依然有值,而且m_pA.nVal的值还从0变成了1?为了搞明白这个问题,我们先来看下DebugTest的结构:
class DebugTest
{
static int nCount = 0;
public DebugTest()
{
m_pA = new A();
}
public class A
{
public A()
{
nVal = DebugTest.nCount++;
}
int nVal;
}
public A m_pA;
A AVal
{
get
{
if (null == m_pA)
{
m_pA = new A();
}
return m_pA;
}
}
}
可以看到,DebugTest中有一个类型为A的Public变量,有一个返回A的AVal属性,并且AVal这个属性并不是单纯的返回m_pA,他有多做一个m_pA为null的话,就创建一个A实例的判断。每创建一个A类对象,对象实例中的nVal值就加1。
聪明如你,可能看到这就领悟到了,难道是在把m_pA赋为null后,又自动创建了一个A对象?没错!但问题是在哪里,什么时候创建的呢?我们再看一张图就明白了:
原因就是:在执行obj.m_pA = null;这行代码前,变量查看窗口已经对obj实例进行了展开,执行完这行代码后会刷新变量查看窗口中的obj变量,由于它是展开的,所以为了知道AVal的值,VS内部就执行了一遍AVal,然后根据AVal的逻辑,m_pA为空,于是创建了一个新的A类实例赋值给m_pA变量,在新的实例构造的时候,把最新的DebugTest.nCount 值1赋给了A.nVal。这也就解释了我们一开始看到的gif图中奇怪的现象。
其实到这边都还算好理解,坑的是,在新的A类对象构造的时候,既在A的构造函数段不到点(可能是出于如果段到了点,堆栈怎么显示的问题?),也没有任何的提示。所以如果你不知道这个坑又刚好不幸中招的话,那么你在断掉调试的时候可能就会发生很多完全理解的问题,这得取决于你在查看变量的展开类的属性中做了什么事。有可能是引用赋值为null后又有值了,有可能是某个变量突然莫名其妙的变化了等等。
如果以后发生了直接运行没什么问题,但是断掉调试的时候莫名其妙的情况,可以往这个方向上先考虑一下,排除是因为查看变量展开导致调用了属性的问题。