离境的时候,觉得北京首都机场的国际安检很弱智。首先,在明明没有人排队的情况下,就把长蛇阵拉起来了,这证明排队从来没被很好地管理过。国外的长蛇阵,甚至是广州的各大银行里的,都是先放好若干横排,但是留两边的直行通道,随着排队人数增加,再把通道逐步封闭,这样排队就转到横排里了。在北京机场,根本没有直行通道这回事,再少人也要绕啊绕啊绕,太过弱智了。
过安检门的时候,首先要看登机牌,我们是自助打印登机牌的,一开始安检人员又看不懂这是否是合法通行证,又要确认一下。因为我们是去美国的,特意要求我们要脱鞋,也就是去其它国家可能不用脱鞋检查啦?那就是要根据机票来决定安检严谨程度啦?这样效率不低才怪呢。现在在登机之前,还有一个液体物品的检查,也就相当于两次安检了。我们到得很早,见到第二次检查的人由队长带着来打登机口前,然后一轮训话,说今天开始严查,所以大家不能随便开包看看问问就行,一定要认真查。结果呢?开包之后,还是很随便看看就行了。
在美国入境时,又是长蛇阵的排队,接着是入境面谈,问了一下来干什么的和要停留多久。开头我在海关申报单选了非business,结果入境官员说,别人付钱让你来的就是business,我说那就business吧。我可从来没想过,作为一个学生,有人出钱让我来开会,这也叫做business。在领到托运行李后,又被人“随机”扔去了过X光检查,真是不幸,不过X光检查的黑人倒好人,看到我是到Microsoft的,就很欢迎的样子,可能最近几天到Microsoft的人太多了,而Microsoft又是Seattle的支柱吧。我的箱子有北京托运来的标记,他就问我北京是不是在紧张准备奥运。
之后就出去了,就这么简单。当然,也有朋友被拦下来的,估计因为他在日本买了个新相机,然后未出关就在那里狂拍照,哈哈。
2008年4月17日 星期四
飞到美国了
2008年4月12日 星期六
黄金周
记得某几个学期,课非常少,一个星期就上两三天课,于是我们说“周周都是黄金周”。其实怎样才算是黄金周呢?我觉得你要是数着日子过,每一天都是回忆的,那才是黄金周。在大学里天天睡觉,又不记得梦见什么,上网太多导致时空感错落,这显然不是黄金周。
明天就飞西雅图了,非常向往,但也非常留恋在北京的不到5天。为什么呢?因为这5天很早就计划好了,从第1天开始你就是倒数着过,每天很清楚记得自己干过什么,因此印象特别深刻。举个例子,同样是在北京,我在百度实习3个月,印象就要浅一些,大概只记得第几周做过什么。这不是因为上班的问题,而是因为我站在第1天向前看就是12周,按周来倒数。如果我在百度的实习是5天,那么我也会清晰记住每天干了什么。
因此,黄金周在于日子倒数着过,而最好的方式就是去旅行。在北京这5天没有怎么出去玩,明天飞到西雅图一定要抓住唯一大家都有空外出的一天,去爽一下!另外期待暑假的德国之旅,如果能去成的话。
广州地铁三号线的算术陷阱
三号线作了一个奇怪的设计,就是一开始的列车只有三节,然后说将来要扩容了再加车厢。加车厢的问题在于,一旦要加一节车厢,现有的列车都必须同时加一节,这样才能确保开闭的屏蔽门是统一的。否则一列短车一列长车的话,乘客在长车车厢位置候车,发现来的是短车,他正对着的那个屏蔽门不开,那就很郁闷了。
这种设计,鼓励的是加独立的列车,而不是加车厢。然而,列车加得越多,加一节车厢的成本也就越高,因为同时要加一节车厢的列车数多了。这就变成了,列车越多,越不鼓励加车厢,越倾向于再加独立的列车。最终,整条线路已经无法再加新的列车时,列出数肯定已经相当多了,这时候加一节车厢的成本就会很大,因此加车厢的可能性反而很低。
我觉得唯一的可能就是,在列车很多的时候选择把短列车重新分组为长列车,多余的机车没所谓,反正机车肯定有冗余的,这样子才能避开这个算术陷阱。
2008年4月3日 星期四
救救 Web Developers ,拒绝 IE6 !
这是最近的一场于web developers相关的campaign,官方站点是SaveTheDevelopes.org。在上面你可以下载一段脚本和对应的图片,放到你自己的网站上,然后只要有用户使用IE6访问你的网站,他就会看到一个小小的提示框,建议他升级到IE7或选用其它非IE浏览器。当然,你也可以直接引用SaveTheDevelopers.org上面的脚本文件,但大家都知道潜在的风险,因此最好不要这样做,如果你有自己的空间的话最好还是下载下来放到自己的空间上。
为了支持这个campaign,我特意为他们做了一个中文翻译。我本来是想和aw一起做的,不过他们把内容发给我的那个晚上找不到aw,于是有翻译问题就直接找Junyu问了,最后在Junyu的帮忙下把翻译搞定了。我在想是不是应该多做一个粤语翻译,这样会比较好玩,反正也就一个晚上的事情而已。
2008年3月24日 星期一
深入理解 ASP.NET 动态控件 (Part 6 - 模板控件)
在之前的文章中,我极力推荐大家使用Repeater和MultiView这类TemplateControl,为什么呢?因为只有这样做,才算是符合MVP或MVC模式。(到底是MVP还是MVC,这视乎你选用什么呈现引擎了。)
虽然我们要动态创建控件,但实际上这部分控件仍然属于View的部分,我们应该尽量采用ASPX的声明性名义来描述这些控件,避免用C#代码来创建控件、设置属性并添加为子控件。就拿最简单的例子来说,创建一个LinkButton,通常我们都需要设置它的ID、Text、OnClick属性/事件,甚至还要设置OnCommand、CommandName、CommandArgument等属性/事件,那就是大概3到5个属性了,用ASPX来声明只需要1~2行代码,而用C#代码则需要写至少5行(把new和Add()也算上的话),由此可见在定义控件这类声明上ASPX比C#代码的可读性要高。
接下来,我们来研究一下TemplateControl是如何工作的,这自然要从如何编写一个TemplateControl讲起。
编写模板控件
在这里,我们假设要编写一个SimpleRepeater控件,自身不支持数据绑定,只有唯一一个名为ItemTemplate的模板,并且就按照Count属性指定的次数重复出现该模板。首先,让我们来定义这两个属性:
public ITemplate ItemTemplate { get; set; }
public int Count { get; set; }
因为ItemTemplate属性不是以键值对的形式在SimpleRepeater的声明中给出的,而是以内嵌一对标签的方式定义的,因此我们需要让解释器去把<ItemTemplate>...</ItemTemplate>中间的内容读取出来,并把解释结果作为ItemTemplate属性的值处理。这时候,我们就需要为SimpleRepeater类加上ParseChildrenAttribute,也就是这样子:
[ParseChildren(true)] public class SimpleRepeater {...}
最后,我们需要重载一下CreateChildControl()方法,把ItemTemplate的内容作为子控件添加到SimpleRepeater之内:
protected override void CreateChildControls()
{
if (ItemTemplate != null)
{
Controls.Clear();
for (int i = 0; i < Count; i++)
{
Control control = new Control();
ItemTemplate.InstantiateIn(control);
Controls.Add(control);
}
}
}
这段代码的用意应该是相当清晰的了,就是循环Count指定那么多次,每次循环创建一个空白的Control,用ItemTemplate.InstantiateIn()方法填充它,最后把它添加到SimpleRepeater.Controls里面,就那么简单。那么这个神秘的InstantiateIn()方法到底是干什么的呢?后面来解释。
编译模板控件
在之前的《深入理解 ASP.NET 动态控件 (Part 2 - 编译过程)》里面,我详细地解释了ASP.NET 2.0的编译模型。在《深入理解 ASP.NET 动态控件 (Part 5 - 编译实验)》中,我们又做了一个动手实验,亲眼看到了ASPX和C#代码是如何编译到一起的。现在让我们来看看当碰上模板控件时,代码会被如何编译吧。我们把上面编写的SimpleRepeater注册到页面上,前缀为ctrl,并且编写如下一段代码:
<ctrl:SimpleRepeater ID="SimpleRepeater1" Count="10" runat="server">
<ItemTemplate>
<asp:Button ID="Button1" Text="Button" runat="server" />
</ItemTemplate>
</ctrl:SimpleRepeater>
然后还是用aspnet_compiler编译一下,并且用Reflector打开编译出来的dll看看。我们可以看到构造SimpleRepeater实例是通过这样一个语句完成的:
return new SimpleRepeater { ItemTemplate = new CompiledTemplateBuilder(new BuildTemplateMethod(this.__BuildControl__control1)), Count = 10 };
这个语句其实就是一个普通的new语句,并且给ItemTemplate和Count两个属性赋值了,唯一值得关注的就是ItemTemplate的值。ItemTemplate的类型是ITemplate,因此任何实现了ITemplate接口的类都可以复制给它,然而我们却从来没指定过到底要赋值什么类型的实例给它,因为这已经由ASP.NET帮我们想好了,假若我们不指定的话,那就是CompiledTemplateBuilder类型。还是熟悉的Builder模式,它只需要已委托的形式接受一个实例化ITemplate的函数,然后就能返回实例化好的ITemplate控件子树。可能你会问,既然我已经有实例化ITemplate的函数,干什么要先传给你CompiledTemplateBuilder,让你来调用一下,再把实例化好的给我?我自己实例化不好吗?在此,ASP.NET引擎的做法只是为了保持Builder模式的一致性,处处用Builder模式来分离逻辑而已。
那么这个用于实例化ITemplate的函数从哪来呢?在解释器进入到<ItemTemplate>...</ItemTemplate>内部时,它会继续层层构建Builder模式,就如同在整个页面内执行的一样。因此,整个<ItemTemplate>...</ItemTemplate>会被解释器转化为一个函数,它也是通过层层调用内部函数完成自身的控件子树的构建,传递给BuilderTemplateBuilder构造函数的委托正是指向此函数。
因此,模板控件里面的内容将如同模板外的内容一样,被无缝地解释和构建到一起来。
INamingContainer
如果你查看SimpleRepeater输出的HTML代码,你会发现里面有10个<input id="Button1" name="Button1" type="button" value="Button" />。我们都知道,重复的id是不符合标准的,因此我们需要通过INamingContainer把这个问题解决掉。因为重复的控件是ITemplate,所以应该对它加上INamingContainer,然而它的实例编译时自动使用了CompiledTemplateBuilder,我们如何把INamingContainer加上去呢?我们就只能把INamingContainer加到它的父控件上面去。此时,我们需要一个实现了INamingContainer的简单控件:
public class SimpleRepeaterItem : System.Web.UI.Control, System.Web.UI.INamingContainer {}
然后我们把SimpleRepeater.CreateChildControls()方法的这个语句:Control control = new Control(),替换为:SimpleRepeaterItem control = new SimpleRepeaterItem()。这样,ITemplate的容器就变成了一个具有INamingContainer接口的控件,这时候各个Button的客户端id就会自动加上其容器的id作为前缀,因为容器的服务器端ID是自动变好的,所以必然是各不相同的,这样就解决了Button客户端id相同的问题。
通过这个例子,我们了解到了编写模板控件时必须为模板的容器加上INamingContainer,因为模板内的控件ID命名是可能重复的,加上INamingContainer就可以避免它们的客户端id重复。
小结
这次的文章解释了为什么我们应该尽量使用模板控件来实现动态控件,并且也说明了如何编写自己的模板控件,以及模板控件最终是被如何编译为Builder模式的代码的。
2008年3月22日 星期六
每个 blog 都会有一堆 draft posts
lulu说她有个blog post选题的文件夹,我也有一堆blog post都draft存放于各处,可能是邮箱中,可能是Blogger的草稿中,也可能是Windows Live Writer等软件的草稿中。通常,一篇写得好的长文章都不是即兴而作的,而是用draft慢慢写出来的。有点点feel的时候,要逼着自己写成一篇完整的文章很难,反而当有几个draft都是同一个相近的题目时,可以合并在一起写成一篇好文章。
这估计是因为,如果只是有一点灵感,这是不全面的,很难自圆其说。当几个灵感合并在一起,就能看到概貌了,前因后果清晰了,正方反方都出场了,自然写出来的文章就会显得内容丰富一些。
2008年3月21日 星期五
深入理解 ASP.NET 动态控件 (Part 5 - 编译实验)
这次的文章是一个小小的动手实验,你需要准备好Visual Studio 2005或者Visual Studio 2008,以及最新版本的Reflector。通过这次的实验,你将对ASPX与C#代码如何合并编译为一个dll代码有所理解。
在实验开始之前,首先来一个小问题:如果不允许你使用ASPX,要你完全使用C#代码写一个具备复杂控件树的页面你会怎么写?把声明控件的代码都放在Page_Load里面吗?或者有更好的代码编写方法?先想想这个问题,然后继续往下看。
实验的第一步,也就是在Visual Studio里面创建一个ASP.NET项目,并编写一个简单的ASPX页面。例如下面这个例子:(以下代码仅包括HtmlForm内的主体部分)
<asp:MultiView ID="MultiView1" runat="server">
<asp:View ID="View1" runat="server">
<div>Please choose either of the followings:</div>
<asp:RadioButton ID="RadioButton1" runat="server" />
<asp:RadioButton ID="RadioButton2" runat="server" />
</asp:View>
<asp:View ID="View2" runat="server">
<div>Please choose any of the following:</div>
<asp:CheckBox ID="CheckBox1" runat="server" />
<asp:CheckBox ID="CheckBox2" runat="server" />
</asp:View>
</asp:MultiView>
在这个例子中,我们构建了一个简单的控件树,同时又不至于过于复杂,确保了编译出来的代码相对简单一些。接下来我们就需要将它编译了,最简单的手动编译方法就是用ASP.NET 2.0自带的aspnet_compiler.exe,这个文件默认会在这个目录中:C:\Windows\Microsoft.NET\Framework\v2.0.50727。你可以使用aspnet_compiler -h来查看完整的帮助,例如编译一个IIS默认站点中的ASP.NET子站点可以使用这样的代码:
aspnet_compiler -v / -p C:\inetpub\wwwroot\site C:\output\site
接下来,我们到输出目录的bin子目录里把dll抓到Reflector里面看看吧。你会看到这个dll里面有三个namespace,分别是-(在Reflector中代表没有namespace)、__ASP、ASP。假设你编译的站点有一个Default.aspx,那么在无namespace的类当中就会有一个_Default的类,对应的就是Default.aspx.cs编译出来的类。大家应该还记得《深入理解 ASP.NET 动态控件 (Part 2 - 编译过程)》里面提到的,直接继承自Page的类是用后台代码编译出来的,_Default类就是这样一个具体例子了。我们打开_Default类来看看,就会发现MultiView1已经是其成员了,为什么呢?MultiView1仅仅在ASPX中声明,没有在C#中声明啊。回头看看《深入理解 ASP.NET 动态控件 (Part 2 - 编译过程)》就能解释了这种现象——Default.aspx.cs是标记为partial的,而在你手动编辑的文件中,这是唯一一个partial,另外一个partial由编译器根据Default.aspx自动生成,编译器解释完Default.aspx后在自动生成的partial中定义了MultiView1,因此两个partial合并编译后,_Default类自然就有了MultiView1这个成员了。
接下来,我们再看看ASP这个namespace下的default_aspx类,这个是ASPX文件继承自上述_Default类后编译的结果,它完整表述了ASPX文件中整个控件树的逻辑,而不仅仅是一个包含一堆成员控件定义的Page派生类。这个类的执行入口是FrameworkInitialize()方法,它通过调用__BuildControlTree()方法来构建控件树。在这个方法里面,你可以看到<!DOCTYPE ...>这样的字符串是被解释为LiteralControl的,LiteralControl在Render()时就会把这段文本原样输出。同时你还可以看到,它调用了另外两个方法,分别用来构建HtmlHead和HtmlForm,这两个方法通过类似的形式继续调用其他方法来构建更深层次的控件。
通过阅读default_aspx类的代码,你已经能够理解ASPX的控件树是如何转化为C#代码的了——采用的正是Builder模式。了解到这一点,这次动手试验的目的也就达到了。如果你看到文章开头的那个问题时,你已经想到了使用Builder模式,那么此时也就验证了你的想法是完全正确的。
下一次的文章将是与TemplateControl相关的,我们将继续动手做一些小实验,敬请期待。欢迎订阅我的blog:
2008年3月14日 星期五
在校学生找实习、找工作、了解企业情况等等等等
因为时不时就有低年级的同学跑来问我这类问题,所以我干脆写篇文章好了。
信息获取
最先想到的,也是最重要的,是你想干什么,而不是你父母想你去干什么,或者哪个赚钱之类的。在计算机系里面,你总能碰到一些对这个行业没什么感觉的人,他们会说当年填报志愿的时候根本没什么喜欢不喜欢可言,于是在父母驱使下或者金钱诱惑下就报了计算机系。显然你不想犯这类错误,因此第一步是弄清楚你想要什么,或者说,有什么是你可以选择不要的。世界上没有十全十美的事情,所以一切都是权衡取舍(trade off),这个思想贯穿着计算机体系设计的方方面面,缺乏这种思想肯定会导致你在这个行业里发展受到限制,所以首先你要想好的就是你将来的职业发展取什么而舍什么。
在你先清晰了解自己之后,才有如何了解企业实际情况的问题。如果说第一个问题严重依赖于你个人悟性的话,那么第二个问题就依赖于你的人脉了,至少是你的外向程度和活跃程度。
一个企业,其自身对外的公关肯定只会说好话,就算没有任何夸张成分,也不会让你看到这个企业内部任何的不足。因此,如果你需要知道一些细节,看看这企业是否如你想象中那么好,你就必须认识在这家企业里面的员工,通过他们好好了解一下。如果你是一个在学校里已经很活跃的学生,整天在BBS上板聊,或者参加各种学生社团的活动,那么你肯定有机会认识到不少比你高一两个年级的同学,看看他们里面有没有一些正好在你想去的企业工作的,联系他们了解一下企业的情况,例如工资和各种福利怎么算啊,工作与学习的氛围如何啊,以及他们是否也认为你适合这个企业。
如果有机会多问几个人的话,最好都问问,并且以人生经历和价值取向与你相近的人的说法为主要参考。这是因为,在同一家企业里面,不同的员工看到它不同的方面,并且都带有自己的主观想法,因此得出的评价可能截然不同。这就好像假如你问我北京是否好住,我肯定说不好住,因为我看重的是商业环境,我觉得北京的服务业经常让我失望,因此就这样说。但肯定也有人很喜欢住北京的,他看重其他一些因素,并且觉得我看重的那些因素可以忽略不计。如果你确实很想去一家企业,就应该多找几个员工了解情况,特别是头脑清晰能看清楚自己所在企业优劣势的人。如果你碰到一个已经被洗脑的员工,那么问什么也没用,反正他就只能帮你洗脑……
联系途径
根据往年的经验,现在正是2009年毕业的学生找实习的时候,甚至有2010年毕业的学生也提前开始找实习了。积极的人肯定早已开始四处打听消息,包括各大公司招聘什么实习职位,往年难度如何,转正比例多少,等等。在这里,我就要“曝光”我身边的一些朋友了,大家不要怪我“出卖”朋友了哦,因为谁都知道成功把有才华的人推荐给自己公司的意义所在。
在这里,我主要说的是BGM(Baidu、Google、Microsoft的意思,不是background music),如果你对其他IT企业有兴趣的话,也可以找我帮忙,只要我认识该企业的人,我会充当一下“路由”的角色为你“尽力服务”的。
Baidu
如果分别把BGM比作人的话,Baidu是一个典型的中国年轻人,一点都不张扬。就如同在中国随手抓一个ASP.NET MVP问他,“你是不是很熟悉页面生命周期”,他可能回答道,“懂一点吧,有什么问题你先说说看。”虽然从资源(resources)的级别来说,Baidu是很难和Google、Microsoft去比的,但是开放程度(openness)还是是相当高的,你可以做自己喜欢做的项目,可以获取到项目所需的资源。
想去Baidu的可以找我,或者布丁,先了解一下。当然,如果你来找我的话,最好你也能说服我为什么值得推荐你。我主要做Web前端开发的,而布丁则是做后端开发的,所以如果你想申请这两个方面的职位,可以直接找我们了解Baidu现在所采用的技术或者流程。如果是其他职位,也可以帮你联系其他同事问问。完整的实习职位列表,请看这里。
Google是一个典型的美国年轻人,你不问他也会很主动地把他的优势展示给你看。在我看来,Google最大的诱惑不在于公司内的“饮食娱乐”项目(虽然这些也很吸引),而是作为一般的中国员工也可以随便跑到美国总部去,顺道参加美国举行的一些会议或者讲座。看看Junyu同学就跑到Austin去开SXSW了。
如果想去Google的话,去找Junyu问问吧。别告诉我找不到他的联系方式,我通常只链接别人的blog,因为该URL就是个人的标识(即使抛开OpenID不谈),拿着这个URL你手上就已经掌握了搜索他联系方式的一切资料。Junyu喜欢把人拐卖进Google里,想了解招聘职位什么的,找他就好了。
Microsoft
Microsoft是个稳重的美国中年男人,Google所晒的东西,Microsoft会暗自想着“我当年又不是没晒过,现在我已经不屑于晒了”。很成熟的流程,健全的管理体制,都很好地说明了这一点。
貌似我没有很熟的朋友在Microsoft,不算太熟那些又不敢晒出来,所以想找人推荐的话还是联系我,然后我再帮你联系吧。想了解的话,可以找Jeffrey或者Dflying这两位老员工加现任MVP,他们现在不再是员工了,可能观点会更加客观一些。
其他事情
据我所知,很多人也是准备找实习了,才想到要写简历的。这是一项需要创意的工作,在此我提供我的简历和Junyu的简历给大家作为参考。虽然我们的网站下方都写着Creative Commons License,不过不建议你直接使用别人设计好的模板。特别是,假如你想申请Web Developer/Designer类别的职位的话,你可不能够错过这个机会好好展示你的设计风格和编码艺术。
最后,祝大家在08年里都能找到自己喜欢的工作学习环境,好好享受生活。