作者:bartholomew 来源:博客园   酷勤网收集 2008-05-17

摘要
  1、在调试过程中打开了Autos窗口;2、将simpleClass.OneField添加到了Watch中;3、运行到“simpleClass.SecondField = "me";”这一行时,鼠标移到对象“simpleClass”上,在弹出的信息显示层中点击“+”,查看一下“simpleClass”对象……

今天在调试同事的一段代码时,发现有一段代码调试时怎么也进不去,难道遇到了Visual Studio的调试禁区?

简化后的代码如下:

    class Program
    
{
        
static void Main(string[] args)
        
{
            SimpleClass simpleClass 
= new SimpleClass();
            
string myString = simpleClass.OneField;
        }

    }


    
public class SimpleClass
    
{
        
private string _oneField = string.Empty;

        
public string OneField
        
{
            
get
            
{
                
if (_oneField.Length == 0)
                
{
                    _oneField 
= GetMyString();
                }


                
return _oneField;
            }

        }


        
private string GetMyString()
        
{
            
return "just for test~";
        }

    }

无法调试的代码即“_oneField = GetMyString();”这一行。

大家可以自行调试,会发现是因为_oneField这个变量总是被莫名其妙的赋上值了,但明明我们还没有给它赋值……

和同事讨论过后,还是没有得出明确的结论,只是推测可能是因为Visual Studio在调试的过程中,自动扫描对象中的属性,然后自行运行属性的get操作~

不知道大家有没有遇到这种情况,虽然最终通过把OneField属性改写成函数解决了这个问题,但还是有些不爽,难道在Visual Studio就真的没有办法调试如上代码?

更新结论:
经过大家的反馈,我重复试了多次,总结如下,欢迎大家反馈意见——
要重现我以上的问题,可按如下步骤操作:在“SimpleClass simpleClass = new SimpleClass();”这一行加上断点,然后逐行调试,并且(请注意,这一点很重要,相信很多兄弟和我的调试结果不一样,可能就是因为这个原因),满足以下任一条件:
1、在调试过程中打开了Autos窗口
2、将simpleClass.OneField添加到了Watch中
3、运行到“simpleClass.SecondField = "me";”这一行时,鼠标移到对象“simpleClass”上,在弹出的信息显示层中点击“+”,查看一下“simpleClass”对象的各属性值,嗯,这个操作的作用和打开Autos窗口一样)
那么,最终是无法到达“_oneField = GetMyString();”这一行的;


进一步更新:
本以为以上发现的问题不会影响程序运行的结果,但却意外发现有时也会影响到程序最终运行的结果——

代码如下:

    class Program
    
{
        
static void Main(string[] args)
        
{
            SimpleClass simpleClass 
= new SimpleClass();
            simpleClass.SecondField 
= "me";
            
string myString = simpleClass.OneField;
            Console.WriteLine(myString);

            Console.Read();
        }

    }


    
public class SimpleClass
    
{
        
private string _oneField = string.Empty;
        
private string _secondField = string.Empty;

        
public string OneField
        
{
            
get
            
{
                
if (_oneField.Length == 0)
                
{
                    _oneField 
= GetMyString();
                }


                
return _oneField;
            }

        }


        
public string SecondField
        
{
            
get return _secondField; }
            
set { _secondField = value; }
        }


        
private string GetMyString()
        
{
            
return "SecondField:" + SecondField;
        }

    }

以上这段代码正确的输出应该是:“SecondField:me”。

但如果我们在调试中这样做了的话。。。
嗯,还是从“SimpleClass simpleClass = new SimpleClass();”这一行开始逐行调试,运行到“simpleClass.SecondField = "me";”这一行时(注意,一定要在运行“simpleClass.SecondField = "me";”这一语句之前进行以下操作),鼠标移到对象“simpleClass”上,在弹出的信息显示层中点击“+”,查看一下“simpleClass”对象的各属性值,然后,OK,F5吧,你会发现,最后的输出变成了:“SecondField:”!!!

到目前为止最好的解决办法:
Options -> Debugging -> General -> Enabled property evaluation and other implicit function calls
去掉这个选项的选中状态!
然后,以上所有的问题都得以解决——这个世界,终于清静了。。。。。。

Feedback

#1楼  2008-05-15 22:54 Gray Zhang      

我在VS2008中可以下断点啊……

#2楼 [楼主] 2008-05-15 22:57 bartholomew      

你确定可以跟到_oneField = GetMyString();这一行?!

当然,在这一行打断点肯定是可以的,只是调试时到不了这样一行而已……

#3楼  2008-05-15 22:58 zeus2      

证明.NET在创建类的时候把所有属性值缓存起来把Get过程都调用了一遍。

#4楼  2008-05-15 23:24 Angel Lucifer      

俺在VS2008中也可以调试。
跟踪到楼主所指的那行没有问题。

#5楼 [楼主] 2008-05-15 23:29 bartholomew      

Gray Zhang和Angel Lucifer 两位兄弟确定是用我文中的代码进行调试的吗?

我也是在vs2008中试的啊,但怎么也调不到_oneField = GetMyString();这一行!

#6楼  2008-05-15 23:31 Angel Lucifer      

俺是直接Copy的。代码一字不差。

#7楼 [楼主] 2008-05-15 23:31 bartholomew      

to zeus2:好像是这样,不然无法解释啊~

#8楼 [楼主] 2008-05-15 23:32 bartholomew      

@Angel Lucifer
。。。
你运行到_oneField = GetMyString();这一行的上一行时,_oneField中还是没值?_oneField.Length == 0??

#9楼 [楼主] 2008-05-15 23:39 bartholomew      

进一步发现问题了:
如果只在_oneField = GetMyString();这一行直接加断点,可以调试到这一行;

但如果在SimpleClass simpleClass = new SimpleClass();这一行加断点,然后逐行调试,那么,不管在_oneField = GetMyString();这一行是否加了断点,都无法调试到这一行

#10楼  2008-05-15 23:41 齐世昌      

我在VS2005中调试也没有你说的问题啊

#11楼  2008-05-15 23:43 Angel Lucifer      

@bartholomew
我也发现这个问题了。

#12楼  2008-05-15 23:47 Angel Lucifer      

我靠,又重新Debug了一遍。
怎么走都会走到楼主说的那一行。
晕了。

#13楼  2008-05-15 23:52 Angel Lucifer      

会不会跟CPU指令乱序执行有关系?

#14楼 [楼主] 2008-05-15 23:54 bartholomew      

@Angel Lucifer
我也有点晕了,我试的结果:
在SimpleClass simpleClass = new SimpleClass();和_oneField = GetMyString();都加上断点的话,如果一路F10下去,到不了第二个断点,但如果一路F5下去,就可以到第二个断点。。。

#15楼  2008-05-16 00:02 无常      

恭喜你,遇到个调试器的BUG~

#16楼  2008-05-16 00:05 Angel Lucifer      

俺自从按了F11逐语句调试后,怎么样都会Debug到第二个断点了,呵呵。
这个才算正常吧。

#17楼 [楼主] 2008-05-16 00:09 bartholomew      

@Angel Lucifer
不会吧,我一路按F11也到不了第二个断点啊~

#18楼  2008-05-16 00:14 Angel Lucifer      

@bartholomew
晕。
看来只能寄希望于精通Debug的牛人来解答了。

#19楼 [楼主] 2008-05-16 00:35 bartholomew      

@Angel Lucifer
我把文章改了下,希望迎来大牛的权威解释~

#20楼  2008-05-16 03:27 yuchen

调试器有超前意识 知道OneField不能为空就提前给值了 呵呵

#21楼  2008-05-16 04:31 Q.yuhen

可能是 IDE 捣的鬼,当 string myString = simpleClass.OneField; 被中断时,IDE 需要显示环境变量(locals,左下角),这其中就包括了 OneField 的值,因此 get_OneField 已经会被隐式执行。当你继续时,自然不会再次执行 _oneField = GetMyString(); 这行,也就不会触发断点了。

我以前碰到过类似情况。 http://www.rainsts.net/article.asp?id=45

#22楼  2008-05-16 07:31 wjx

你用windbg试试就知道了,不可能到不了。

#23楼  2008-05-16 08:31 Anders Liu      

醒目:牛人已在21楼出现

#24楼  2008-05-16 08:59 allies

学习了~真是不看不知道啊 21楼的解释很详细了!

#25楼  2008-05-16 09:03 Allen Lee      

我复制了你的第一份代码,把断点设在你所说的那一行,结果Visual Studio能正常在这里中断,并且可以继续单步进入GetMyString()方法,_oneField字段的值也正常。(我用的是Visual Studio 2008)

#26楼  2008-05-16 09:06 Allen Lee      

至于第二个代码,我的输出结果是:

SecondField:me

#27楼  2008-05-16 09:15 hbb0b0

我的也正常着

#28楼  2008-05-16 09:16 球球      

正常的,因为_oneField在你看属性值的时候已经被执行过了

#29楼  2008-05-16 09:18 bangbang

博主用的是vs2008吗?

#30楼  2008-05-16 09:23 BAsil      

同意雨痕的,我也碰到过,是由于监视窗口可能赋值了该变量,导致代码不再执行,可以把监视窗口晴空。

#31楼  2008-05-16 09:28 bangbang

21果然大牛,发现debug可以去掉属性求值和隐式函数调用的,去掉后一切正常.

#32楼 [楼主] 2008-05-16 09:51 bartholomew      

@Allen Lee
我把文章修改了一下,你可以再看一下我更新的部分~

第一份代码,你能够正常调试到那一行的原因,我猜是因为你在调试过程中,Autos窗口没有开启。。。

至于第二份代码,你的输出结果正常的原因,我猜测是因为你不是在运行“simpleClass.SecondField = "me";”这一语句之前进行以下操作(即鼠标移上去查看属性值)的。。。

你可以验证一下,不知道我的猜测对不对~

#33楼 [楼主] 2008-05-16 09:52 bartholomew      

@球球
对VS来说是正常操作,但是我在调试的时候,不想让它被自动赋值啊啊啊……

#34楼 [楼主] 2008-05-16 09:54 bartholomew      

@bangbang
用的是VS2008

你说的“debug可以去掉属性求值和隐式函数调用”,这个是在哪儿可以自行设置吗?

#35楼 [楼主] 2008-05-16 09:56 bartholomew      

@BAsil
对,不要监视那个属性,而且,不能开启Autos窗口

PS:雨痕是谁?我怎么没看见他的留言呢~:)

#36楼 [楼主] 2008-05-16 09:58 bartholomew      

@Q.yuhen
谢谢你的回复,很详细!
唉,这种错误会在不经意间碰到,有时会让人很郁闷……

#37楼  2008-05-16 09:58 Q.yuhen

Options -> Debugging -> General -> Enabled property evaluation and other implicit function calls

#38楼 [楼主] 2008-05-16 10:05 bartholomew      

@Q.yuhen
OK!
这个方法很管用,设置后,似乎我提到的问题都解决了!非常感谢!

不过,更改这个设置后,会不会造成一些不便呢~?

#39楼  2008-05-16 10:06 PeterLeePersonal

楼主你好, 我是CTI VS IDE 组的TESTER, 根据你描述的情况, 我进行了试验.
最终确定你忽略了一个VS2008的设置, 请依次点击: Tools->Options->Debugging->General
找到一个叫Step Over Properties and Operators(Managed Only)选项, 并取消选中这个选项.
然后进行调试时就可以调试到Property的内部去了.
如果还有疑问, 你发邮件到PeterLeePersonal@gmail.com

#40楼  2008-05-16 10:13 Justin      

这个问题不能怪VS,更不是什么VS的调试禁区,还是楼主自己搞晕了。
1、这段代码在逻辑上有问题,是导致这次灵异事件的一个主要原因;
public string OneField
{
get
{
if (_oneField.Length == 0)
{
_oneField = GetMyString();
}

return _oneField;
}
}
这段代码决定了OneField只能被执行一次,这么写到是没有问题,但是:
public string SecondField
{
get { return _secondField; }
set { _secondField = value; }
}
这个写法有问题,应该写成:
public string SecondField
{
get { return _secondField; }
set
{
_secondField = value;
_oneField = string.Empty;
}
}
也就是说,每次改变SecondField的时候,要清空OneField,如果这么做,就不会有问题了。
2、VS调试器实际上使用CLR调试API中的Eval方法执行对象中的属性和方法,这是VS调试器为方便开发调试而提供的一个非常实用的功能。也就是说,在你对OneField进行添加到监制、快速监视的时候,其实就已经执行过一遍Get方法了,OneField的长度已经大于0了,此后在任何时候也没有清空OneField,所以就不会再执行_oneField = GetMyString();了,这是楼主自己给自己下的套哦!

综上所述:可以解释所有灵异现象了吧,如果在 simpleClass.SecondField = "me";执行前查看了OneField的值(触发一次Eval方法执行OneField的Get),那么最后结果肯定就是“SecondField:”,此外结果都是“SecondField:me”。

#41楼 [楼主] 2008-05-16 10:28 bartholomew      

@Justin
谢谢你这么详细的回复~

你说的两个问题:
第一,“这段代码在逻辑上有问题”,因为这段代码是别的同事写的,我在调试的时候也发现有些逻辑考虑得不是很严密,给我造成了很大的困扰~不过,我觉得造成我提出的问题的原因,在此处倒不是因为不严密的逻辑,还是VS太智能了,帮我们做了一些操作,而这些操作,却根本调试不进去,呵呵~

第二,我也不想给自己下套啊,这是VS的默认设置给我下的套啊,呵呵

#42楼 [楼主] 2008-05-16 10:29 bartholomew      

@PeterLeePersonal
找不到你说的Step Over Properties and Operators(Managed Only)这个选项。。。

#43楼  2008-05-16 10:54 BAsil      

楼主 21楼就是雨痕阿,呵呵

#44楼  2008-05-16 11:16 Duron800

学习了,雨痕在06年8月就发过类似问题的文章了。

#45楼  2008-05-16 11:52 随风流月      

我很早就遇到过这样的问题 - 大凡能在 IDE 中自动取值的玩意儿都可能有同样的问题.

来自:Visual Studio也有调试禁区?!

分类: 开发工具 管理工具 修炼之道

上一篇:让eclipse和NetBeans共享同一个项目   下一篇:Notepad 2——小巧开源的文本编辑器