我听了之后也同意他的看法,因为自己在写程序的时候,也因为经常忘了这一点而发生类式的“BUG”。回头仔细想了想这个问题,发现类似的还不止DateTime一个,像TimeSpan,DateTimeOffset,甚至于是String的Replace等都是这样的一种情况。非public的方法咱就不说了,没有对外公布里面怎么使用都还可以说得过去,可对外公开的方法还是这样的,就有些不太理解了。
从上面提到的类型不难看出,这些大都是结构,也就是值类型。虽然String类本身是类类型,但是根据MSDN的说法,由于考虑到在使用上的习惯,String在大部分场合的行为都与值类型一至,因此就行为上来看,Sring大部分的行为仍然是符合值类型的行为,包括赋值,比较,参数传递等。至于为什么String的Replace不是作用在本身上,而是返回一个新的String实例,还有一个原因是String实例分配的内存在构造完成后是只读的,因此想要改变这个值就只能重新生成一个新的实例了。String类的这个方法弄明白了后我们再回到之前提到的那些结构上。这些结构的内部都是由一些简单的值类型组成的,而且从声明的成员字段上来看,都是可以随意改变的。
1 public struct DateTimeOffset
2 {
3 // Fields
4 private DateTime m_dateTime;
5 private short m_offsetMinutes;
6
7 }
8
9 public struct DateTime
10 {
11 // Fields
12 private ulong dateData;
13
14 }
15
16 public struct TimeSpan
17 {
18
19 internal long _ticks;
20
21 }
这就更难理解M$在设计这些类型的时候为什么不把相应的方法(主要指的是AddXXX之类的方法)设计成直接作用在调用的实例本身上而要新生成新的实例了?难道就因为这些类型都是结构,也就是说都是值类型的原因?带着这个疑问我又查看了Framework里面其它常用的结构类型,结果又发现了几个不一样的类型。位于System.Drawing名字空间下的Point(F),Rectangle(F),这几个类型也同样是结构,可是在这些类型上的实例方法,比如说Point(F)的,Offset方法。
2 {
3 this .X += dx;
4 this .Y += dy;
5 }
6 public void Offset(Point p)
7 {
8 this .Offset(p.X, p.Y);
9 }
另外还有更“神奇”的是同样是Point(F)里的方法Add却变成了一个静态的方法,而且是重新构造了一个新的Point(F)结构返回,我在这里一点也看不出来为什么这个地方要把Add声明成静态的而不是Point的一个成员方法,功能上这个方法和Offset的那两个方法没有本质上的区别。
2 {
3 return new Point(pt.X + sz.Width, pt.Y + sz.Height);
4 }
同样是结构类型,一部分的方法是把当前对象的值与传入参数运算后构成新的实例返回,而另一部分却直接作用在当前对象本身上,看来上面我猜想的“这样使用的都是结构类型”这种猜测也是不正确的。这两种方式同时出现在Framework里面,感觉好像是随意性比较强,不太符合编程风格的一致性,这只能说明M$自己在写代码的时候也有不遵守规则的地方。
22