Feeds:
文章
评论

Archive for 2008年1月

拿一段录像充数:

自从把小河马在睡觉时候扶坐一次,现在妈妈每天都要求看“坐河马”,而且名目繁多:换件睡衣要看“蓝”坐河马;脑袋摔破了要看“伤疤”坐河马…(嗯,脑袋摔破了一次,自己走路摔跟头一头磕在椅子背上,眉毛下,眼睛上,挂彩了。。。有照片秀的

其实,坐起来然后躺下继续睡挺舒服的:例如早上赖床,在床沿坐了一会,思想斗争还没来得及完毕几乎再次睡着了,这时候能躺下继续呼呼真的很舒服。

Technorati 标记: , ,

Read Full Post »

C# 3.0都出来了,我还在嚼2.0的剩馍馍。不过,闻道有先后,知耻胜于勇,知之为知之,不知为不知,是知也,金砖何厚,玉瓦何薄,天不言自高,地不言自厚,人不言自能,水不言自流。。。扯远了。。。

这篇为学习笔记。

最近看了Eric Lippert给新书"C# 3.0 Design Patterns"写的,颇为C#语言在设计模式上的发展感慨。紧后来又看了Juval Lowy的文章"C# Create Elegant Code With Anonymous Methods, Iterators, And Partial Classes"以及Christian Gross书中的一篇文章"What Does the yield Keyword Really Generate?",对iterator模式以及C#是如何帮助程序员实现这个模式,有了更深入的理解。

笔记以问答形式展开。

问:什么时候用到iterator?

答:

如下代码,简单说,使用foreach关键字进行遍历(笔记中强调遍历,因为被访问的序列既可以是数组、序列、堆又可以是树、图等复杂数据结构)的时候,就用到了iterator。

string[] cities = new string[]{"London", "Beijing", "Cardiff"};
foreach (string city in cities)
{
	Console.Writeline(city);
}

问:什么类型可以实现iterator?

答:

从C#对foreach关键字干了什么,可以看出什么类型可以实现iterator;C#编译器将上面foreach的一段代码展开成如下等价代码:

IEnumerable enumerable = cities;
IEnumerator enumerator = enumerable.GetEnumerator();
using(enumerator)
{
   while(enumerator.MoveNext())
   {
      Trace.WriteLine(enumerator.Current);
   }
}

即要求cities变量是一个继承于接口IEnumerable的类型(本例中,cities是一个string数组),并且实现了该接口的GetEnumerator()函数。

多说一句,看清foreach如何工作的,还可以理解为什么在foreach循环中不允许对被遍历的序列进行添加或删除元素的操作。

问:什么是IEnumerable和IEnumerator接口?

答:

IEnumerable的定义如下:

public interface IEnumerable 
{
   IEnumerator GetEnumerator();
}

IEnumerator的定义如下:

public interface IEnumerator 
{
   object Current{get;}
   bool MoveNext();
   void Reset();
}

IEnumerable和IEnumerator还有泛型的定义(即IEnumerable<T>和IEnumerator<T>),暂不展开。

问:如何实现IEnumerable和IEnumerator?

答:

C#2.0以前,可以通过嵌套类(nested class)实现iterator模式。嵌套类“知道”要被遍历的类的设计,通过维护一个代表状态的变量,实现iterator。

举例:如下代码实现一个继承于IEmerable的类,CityCollection。CityCollection有个string数组型的m_cities成员变量,存了一组城市的名称。可以通过foreach(string city in new CityCollection()){…}来遍历这些城市名称。

1. 实现IEnumerable接口,即GetEnumerator()函数:返回一个MyEnumerator类型的实例。MyEnumerator就是嵌套类,从其定义看,MyEnumerator继承于IEnumerator。

2. 设计嵌套类MyEnumerator:从定义看,MyEnumerator有一个CityCollection型的成员变量m_Collection,该成员通过构造函数初始化为CityCollection的this引用。另外,MyEnumerator有一个整型的成员变量m_Current,这个变量是用来维护遍历状态的。

3. 实现IEnumerator接口,即为嵌套类MyEnumerator实现Current{get;}属性以及MoveNext()和Reset()函数:

  • Reset()函数将状态变量m_Current设为-1;
  • MoveNext()函数首先将状态变量m_Current自加1,并返回一个布尔型值,判断当前状态变量是否小于CityCollection的m_Cities成员变量的数组长度。
  • Current{get;}属性则利用状态变量m_Current的值作为下标返回m_Cities相应的数组成员。

结合上面C#编译器为foreach展开的代码,可以了解嵌套类的MoveNext()函数和Current{get;}属性是如何被调用以实现遍历的。

public class CityCollection : IEnumerable
{
   string[] m_Cities = {"New York","Paris","London"};
   IEnumerator IEnumerable.GetEnumerator()
   {
      return new MyEnumerator(this);
   }
   
   //Nested class definition 
   class MyEnumerator : IEnumerator
   {
      CityCollection m_Collection;
      int m_Current; 
      public MyEnumerator(CityCollection collection)
      {
         m_Collection = collection;
         m_Current = -1;
      }
      void IEnumerator.Reset()
      {
         m_Current = -1;
      }
      bool IEnumerator.MoveNext()
      {
         m_Current++;
         return(m_Current < m_Collection.m_Cities.Length);
      }
      string IEnumerator.Current
      {
         get
         {
            if(m_Current == -1)
               throw new InvalidOperationException();
            return m_Collection.m_Cities[m_Current]; 
         }
      }
      public void Dispose() {}
   }
}

问:C#2.0 以后如何实现IEnumerable和IEnumerator?

答:

C#2.0引入了yield关键字,大大简化了iterator模式的实现。仍以如上CityCollection类为例,看如下代码如何使用yield实现了IEnumerable接口:

public class CityCollection : IEnumerable
{
   string[] m_Cities = {"New York","Paris","London"};
   IEnumerator IEnumerable.GetEnumerator()
   {
      for(int i = 0; i < m_Cities.Length; i ++)
      {
         yield return m_Cities[i];
      }      
   }
}

这次,CityCollection没有设计嵌套函数,仅仅在GetEnumerator()函数中通过yield return的形式顺序返回了m_Cityes所有的数组成员。

问:yield干了什么?

答:

仔细看上例CityCollection实现的GetEnumerator()函数,貌似yield将m_Cities里面所有的数组成员一一收集在一起,封成一个IEnumerator型的变量返回。

yield关键字一定在这里干了些事情,否则无法解释代码返回的是一堆string型的变量,怎么GetEnumerator()就能返回一个IEnumerator呢?我觉得,上面那个对于yield的功能的理解,能方便实现代码:想方设法在GetEnumerator()里面通过yield返回所有你需要遍历的成员,剩下事情让C#编译器去处理吧(这个理念在实现递归遍历算法的时候很关键)。

问:yield到底干了什么?

答:

yield并没有将需要遍历的成员(自己设计实现IEnumerable就是为了可以使用foreach遍历你想遍历的东西)从CityCollection类中收集出来封装成一个IEnumerator。

yield是C#编译器帮助程序员实现iterator设计模式的一个代码生成标志符。实际上,C#编译器看到yield,就生成一堆嵌套类代码,其形式和上面通过嵌套类实现CityCollection的例子类似,其功能也类似:1, 可以访问CityCollection类的成员变量(即需要遍历的成员);2. 维护一个状态变量以实现遍历。

问:yield真生成了一个嵌套类吗?

答:

真的。我们通过MSIL Disassembler来看一下CityCollection编译后的IL。如图:

CityCollection

项目的名字叫StudyYield,看看在CityCollection类下面,有一个叫<System.Collections.IEnumerable.GetEnumerator>d__0的类。这个就是编译器为yield关键字生成的嵌套类。我们看这个嵌套类继承了IEnumerator接口。这个嵌套类实现了MoveNext(),get_Current()(即Current{get;}属性),Reset()三个IEnumerator接口的函数。我们还可以看到该类是sealed,使用了CompilerGenerated属性。

问:能在程序中调用这个为yield生成的嵌套类吗?

答:

不能,这个类的名称中使用了"<"和">"字符,C#代码中不允许使用这两个符号命名。这保证了编译器生成的类不会和代码里面任何类发生命名冲突的问题。

问:yield生成的嵌套类的代码到底是什么样的?

答:

简单说其形式和上面通过嵌套类实现CityCollection的例子一样。Christian Gross给出了一个最简单的和稍微复杂的编译器生成嵌套类的反编译代码(Intermediate Languarage, IL)。他同时怀疑这样的生成代码会不会有什么bug?

总结:

  1. 通过定义某类继承于IEnumerable(或者IEnumerable<T>)并且实现GetEnumerator()函数,可以方便的使用foreach关键字进行遍历操作。
  2. 遍历操作可以通过iterator设计模式实现。设计一个嵌套类(1. 可以访问需遍历的元素;2. 可以维护遍历的状态)来实现iterator设计模式。
  3. C#2.0以后,使用yield关键字方便实现iterator设计模式。
  4. yield生成一组可以在遍历时顺序访问元素的代码(同嵌套类),而不是将被遍历的元素“收集”起来以备访问。
  5. yield是C#编译器在实践规范程序设计模式上的进步。
  6. 笔记未涉及IEnumerable<T>和IEnumerator<T>这两个泛型接口。其实现方式类似。
  7. yield还有yield break的使用方式。yield也有一大堆使用限制,最关键的是yield仅仅在需要实现iterator设计模式时才使用,狭义说,仅在实现IEnumerable或IEnumerable<T>接口时采用。
  8. 使用C#,编译器干越来越多的事情,程序员干越来越少的事情。。。C# 3.0更甚。
  9. John Robbins在他的早期文章Bugslayer: ILDASM is Your New Best Friend中建议通过学习IL查看程序的编译结果来学习C#或者.Net Framework究竟是如何工作的。

 

注:

笔记使用的CityCollection例子来自Juval的文章。对yield IL的分析学习自Christian Gross的文章。

Technorati 标记: , , , , ,

Read Full Post »

自从去年10月份,大病小病的没断,就没有一天小河马安安静静的过去。

咳嗽是家常便饭了,现在咳上两声,挂着满脸鼻涕都已经见怪不怪。

说起鼻涕,先扯两个题外话。

一个是手绢。

云震的鼻涕随时都可能挂着,要是打两个喷嚏,整个嘴巴都糊满了鼻涕。这情况在任何地方都可能发生,爸爸总印象手头找不到手绢应急。和妈妈说了几次手绢少的问题,妈妈才极不情愿的拿出了第三块手绢,保证家里云震小屋,爸爸妈妈大屋和厨房各备一块。

另外是幼儿园。

每天接小孩,云震鼻子和嘴之间加上小脸颊,结着鼻涕嘎巴。自从小河马知道自己抹鼻涕后,又常见一左一右两撇造型。这幼儿园就忙到没功夫给小孩擦一把鼻子?

书归正传。咳嗽鼻涕都是小事,一个很不乐观的规律是,每每到周末,云震的病情就得加重。

妈妈死活不承认这个“规律”,但事实胜于雄辩。往往是一周好好的,到周五晚上开始发烧;大多数时候是周六下午发烧。而且,这烧不会延续很久,多则两天,少则一晚就好了。到了下周一,云震好好的又去上幼儿园了。

爸爸总结了一个原因,家里冷。冻的。妈妈还是死活不承认这个原因。当然,连规律都不承认,何况找到的原因。

但家里冷确实是个事实。温度计显示着16度左右,爸爸在家工作一天手指头冰凉、脚趾头发僵,这些事实妈妈竟然都不承认。

屋子大,厨房有大玻璃,厕所干脆是加修的,连暖气都没有。整个小半层都是冰窖一样。大卧室的玻璃透风,由于是朝街面,房东也解释过了,想改进成双层塑钢的窗子不批准。唯一一间朝阳的屋子是云震的小屋,晴天还算好,阴起天来也冻的透骨。

另外,还要抱怨一下家里的暖气。由于屋子没有接煤气,所以都是用电的。每个屋子一个电暖器。设计的是为了节点省钱:每天在晚12点到早7点烧,使用夜间电价;暖气里面有个大石头,储好热了白天慢慢放。听起来不错的设计,实际很有问题。试问,我们什么时候最需要暖气来加热屋子?答案显然是晚上时间,大致说是下午5点到睡觉前,全家人都在家里呆着。而这个时候,也正是暖气放了一天的热气,几乎都凉了的时候!所有的暖气都是做好了的,白天想开还不能开。爸爸对这样一个愚蠢的设计忍无可忍。

暖气不太好使,那就要保暖吧。这就说到重点了。

住了一年多了,两个卧室的门就一直是开着的。大卧室的两扇窗户被房东加了两扇巨大的玻璃,弄成个假的双层玻璃。新加的窗是向屋里开的,一个窗户前放了电视,一个窗户前是爸爸的桌子,两扇窗户都打不开了。所以要开着门通风。小卧室曾经当作了储藏室,云震来之前,妈妈给彻底清空,并另外铺了一层绒绒的地毯。地毯刚好高了一点,门就不方便开关,索性就一直开着。厨房没有门,冷空气直接灌到每个卧室。

爸爸一口咬定要关门保暖。把小屋的地毯掀起一角,门也可以关上的。

那两天是真的冷,就关着门,屋里的温度也就保持了18度左右。妈妈怀疑保暖的效果。爸爸就把温度计放到厨房,事实证明,厨房只有11度!

妈妈坚持开着卧室的门,除了通风外,还有另外的道理。

小河马自己睡小屋,但夜里半醒的时候,会喊两声。大人过来安抚两下,也就继续睡了。头关门的两天,小孩似乎察觉了门关上了,小喊两声大人没听见,干脆坐起来大哭。这下陷入了关门与开门的矛盾境地。

刚开始的办法是,临睡前,把门打开,让小河马看到门开着。等他睡着了,再把门关上。不过,妈妈不得不睡得更警醒一点。

现在,爸爸的妥协是把门半开。。。

关于这家里卧室关门开门的问题,太爷爷和太太也争了一辈子了。

太太的想法和妈妈一致,屋子要通风。太太更是每天早上就把所有卧室的窗门都大开。而太爷爷容易感冒,比较倾向关上屋子的门,防止受风。

我们做子女的劝过老太太,重点是老头子的身体啊,老头子一感冒就个把月不容易好,我们还是以不让老头子受风为好。另外,北京的空气污染越来越严重了,户外的空气还不一定好过屋子里的。

为此,家里用了加湿器,空气净化器。可老太太就是改不了习惯了,即使家里落了满处的沙子再慢慢擦,也得把开窗开门通风的事每天做到。老爷子呢,因为和老太太一辈子了,早就忍了。

前一段时间看《金婚》,里面也有类似的情节,看来这开门和关门是夫妻必争小事,而且往往是开门者坚持到底。

为着冷的事,全家人没少想办法。姥姥给买了一身棉睡衣,一下子解决了大问题;我们买了个电暖器,晚上就在小屋加强一下温度。英国最冷的天气几乎过去了,明年再说了吧。

连续唠叨了几篇和小河马生病有关的日记,实在因为小孩生病太闹人,“刻骨铭心”。接下来该说说小河马上幼儿园的事情了。

Technorati 标记: , ,

Read Full Post »

本来应该在07年年底完成这篇,但由于圣诞放假,老婆孩子都放假,所以全家都放假了。

其次,和往年不同,今年要感谢的十位人物不再包括家庭亲属,因为家庭亲属给予我的关怀和温暖以及在每日生活中的点点滴滴数不胜数。亲情和爱情并不是到了友情的对立面,只是我绝对可以并列列出两个长长的名单来感激。

名单按照姓氏字典顺序。各位给予我的帮助和关怀以及我的感激之情排名不分先后。

白洁

一如既往,小白年年上榜。大喜事莫过于得千金了。还是那句话,有小白在伦敦,伦敦就如同谢菲尔德或者加的夫一样,如同在外的家。当友情也转变成亲情?

崔阳

列崔阳入榜,我可是权衡很久。唯一原因是,前文说了这个榜单不包括家庭亲属的,而崔阳现在已经是我妹夫了。不过,在大部分的2007年,他还是以我妹妹男朋友形象出现的。首先感激他“收”了我妹妹。这是一位我见一面就认定是好男人的人,有他照顾我们家的姑奶奶,奶奶爷爷大舅舅妈都会很放心。然后感激他在小河马摆脱黑户的事情上给予了无人可及的帮助。以往我认为我爸爸的能量很大,现在看,早已是年轻人的天下了。另外,崔阳可是位羽毛球高手高手高高手,回北京可以跟着混了。可惜,明年他肯定不能上榜了,又一例友情转变成亲情。

冯爽

我很熟悉谢菲尔德,道路熟悉,建筑熟悉。但人却渐渐不熟悉了。仍旧叫一声哥们的,还是冯爽。临近毕业的阶段,大家都在琢磨将来的发展,我很感激这个时候能和冯爽交流意向、机会和任何合作的可能。虽然回谢菲尔德次数越来越少,但每次仍是宾至如归的感觉。明年,他应该是毕业和结婚双喜临门吧!

黄启锋

啥都不说,单单冲启锋转给我们现在住的这套公寓,我们在住一天就要感谢一天。另外,我冒名顶替黄启锋在加的夫大学横行,拿着他的工作证出入办公室、图书馆和羽毛球俱乐部,直到7月份启锋离职。那段时间,别人问我是谁,我说就是“Ping”,但写就是Qifeng Huang,还补充上说大家都叫我Ping,因为叫起来顺口。。。身份上也极其混乱,有时是在读博士研究生,有时就是交换学生,有时还是Post Doc。。。应该说,在我们刚来加的夫的时候,是启锋照顾了我们,现在他又把他在加的夫的全部余热都奉献给了我。

牛建勇

工作需要,建勇要经常国内英国两边跑,于是他就成了我们家的运输大队长。我从来没见过哪个男性朋友对云震这么亲,比自己家里人还亲。于是,打着小河马的旗号,我和河马妈也成为受益者,我岳母也建勇建勇的念叨个不停。投之以桃报之以李,我们一定要帮建勇这个钻石王老五解决个人问题。

彭大卫

在给我们的圣诞贺卡上,他自己用中文写着彭大卫。David Penglly是我们的邻居,我们住3A,他住3B。单身的老头,机械工程师。熟起来之前,他负责打理我们这边房东的几套公寓的琐事。熟起来之后,我是他的IT工程师,而我们无论大事小事,凡涉及动手动家伙的(是修理东西不是打架。。。),都找到他。David是个闲不住的人,放假在家经常跑出来街边溜达,或者就敲敲我的门。他知道我在家学习,所以“打扰”一下,送上几个自己摘的苹果,自己做的蛋糕。要是周五下午,则4点钟肯定过来一起喝上一杯咖啡。

宋国锐

以烟会友,虽然不健康,但真正认得一位投缘的好朋友。国锐也是我们的邻居,隔一个篱笆,门靠门。要不是那天我们俩都到街上抽烟,实在没机会认得。一见如故,绝对是朋友间的缘分。正所谓非常投机,每天抽烟时间也很固定,周围邻里也都看惯了,经常是这俩人站在路边抽烟聊天,还很长时间。老婆对我抽烟一向没大意见,但现在一出门就聊几十分钟有点不可忍,说我是大树。我和国锐自比佟志和大庄的关系,老婆们的事情,伸伸舌头赶紧回家哄就是了。烟只是个开始,我们还有很对健康的共同爱好,打羽毛球,打篮球,打牌。。。

王铮

我一向欣赏仰慕有爱好的人,铮是一位代表。爱好并成为专业,摄影,古典音乐,旅游,样样发烧。铮是那种对朋友肝胆相照的人。我们夫妇在慕尼黑旅行,铮供房供车供餐供书还供人。至今,“小数码”、“不能忍”等话我们还挂在嘴边,这些都是铮的口头禅。祝福铮和HQ在新年开花结果。

魏晓清

打羽毛球成了我在英国坚持最好的运动。在谢菲尔德,在剑桥,在加的夫。魏晓清是加的夫一帮爱好羽毛球运动的人的组织者。每星期订场地,发邮件,管理帐目。逢年过节组织大家聚在一起。我一直没有当面道谢过,但确实由衷的感谢组织者,每个星期坚持的组织者。去过他家里两次,魏晓清夫妇也是爱好生活的人,家里收拾得干净利索,魏夫人还爱好画画,用镜框镶上几幅自己的作品点缀家里。我和老婆说,像魏晓清这样的生活质量,我很羡慕。

俞凯

有些朋友不是整天陪在身边的,但会陪着走过人生很长时间。俞凯是位非常有能量的朋友,天生的领导者,身边总团聚着一些有能力的人。他是那种我有困难的时候自然而然想到要求助的人。对大哥的感激往往不溢于言表,而是一种随时准备着为大哥冲锋陷阵的情绪。

关于家里的事情,再有文章整理总结吧。

Read Full Post »