巴别塔上的雇工


别让ToString函数改变对象的状态
6月 6, 2009, 12:59 上午
Filed under: 技术体会

几天前在Debug的时候碰上一件让人抓狂的事情,为了分析一个bug,我用Visual Studio连上目标进程,在一个函数上设了断点(breakpoint),然后按F10一行一行地看程序状态,很快就发现程序状态不正确,但是又看不出来状态怎么突然不对了。将Visual Studio和目标进程断开,程序状态就恢复正常了,奇怪!难不成薛定锷的猫理论又出现了?Debugger会影响到程序的行为,但是我没有通过Debugger做任何改变程序状态的事情啊。

重复操作了一遍,还是这个现象,只要用Debugger去看一眼,就会出错,但是没有Debugger就没事,这几乎要让人疯掉了!

后来终于想明白是怎么回事,原因就是某个类的作者让ToString()函数改变了对象的状态,这,编译器是容许的,但是相当不应该的,是一个Worst Practice

   1: class Foo
   2: {
   3:     private string _msg;
   4:  
   5:     public int ToStringCount
   6:     {
   7:         get;
   8:         set;
   9:     }
  10:  
  11:     public Foo(string msg)
  12:     {
  13:         _msg = msg;
  14:     }
  15:  
  16:     public override string ToString()
  17:     {
  18:         ++ToStringCount;
  19:         return _msg;
  20:     }
  21:  
  22: }
  23:  
  24: class Program
  25: {
  26:     static void Main(string[] args)
  27:     {
  28:         Foo foo = new Foo("foo");
  29:         Console.WriteLine(foo);
  30:         Console.WriteLine(foo.ToStringCount);
  31:         Console.ReadLine();
  32:     }
  33: }

 

上面的code,在Visual Studio里面按Ctrl+F5直接运行,输出是

foo

1

这在意料之中,但是如果你在Main函数第一行设上断点,F5进入Debug状态,然后一行一行走,走后的结果是什么?这取决于你的Locals Debug Window有没有打开,如果Locals打开了,每Step Over一次,Debugger都会去调用Local Variable的ToString()函数,这样ToString()函数调用的次数就比没有Debugger的调用要多。这就是我碰到问题的根源。

也许你会说,只要不用Debugger不就没问题了吗。但是,从一个类的角度来说,它并不知道自己的实例会在什么场合下如何使用,它也没发知道,所以必须保证自己public出去的方法不管怎么折腾行为都是正确的,让ToString()改变对象的行为违背了这个原则。很可惜C#并没有强制一个函数不能改变对象状态的语法,所以只能靠程序员自己注意了。


发表评论 so far
留下评论



留下评论