2007年10月30日星期二

固定订户与临时订户

从我对FeedBurner订户信息的跟踪看来,我觉得订户可以分为两类,一类是固定订户,一类是临时订户。

使用web reader订阅的,或者使用专用的client reader订阅的,属于固定订户,这些订户数量通常很少减少。另外email订阅的也能算这一类。原因是,用户一旦订阅了,就没什么理由去退订,除非你信息爆炸,flooding到用户受不了了,用户就会退订。反而你发布的频率不高倒的话,用户不一定能察觉到,他们可能已经忘记自己订阅此feed了。总而言之,固定订户的数量是几乎不会减少的,你只要能吸引用户成为固定订户,那么那个数字就是solid的,那就是你的一项real estate(不动产)。

至于另一类用户,则正好相反,它们使用浏览器自带订阅功能,或者是Outlook 2007等非专用client reader。这些订阅量绝对不是solid的,随着用户改变他们的软件使用习惯,订阅也就自然而然的被“取消”了。然而因为这些用户在一段时期内还算是活跃的,因此FeedBurner还是会把他们算上,但你在真正估计自己的feed价值时,应该把这层表面的泡沫给去掉。

最后,因为我的feed订阅数不算大,希望有相当订阅数的作者出来讲讲事实是否如此,例如aw或者猫影

在 C# 中 ("x" == "X") 何时成立?

这个问题初看起来很奇怪,C#就是C#啊,一门严谨的语言,并且字符串是区分大小写的,无论是在什么情况下都有("x" != "X"),这才叫做一致性嘛。事实上,这在以前一直都是成立的,直到.NET Framework 3.5引入了Linq to Sql,这种一致性就被破坏掉了,变成依赖于环境配置了。

想象一下我们对一个Linq to Sql的DataObject编写一个Linq查询,并且where子句包括("x" == "X"),那么该子句会返回true还是false呢?事实上,该查询虽然是一个用C#编写的lamda表达式,然而并不编译为MSIL。Linq to Sql里面的Linq是直接编译为SQL语句的,因此("x" == "X")会直接变成SQL里面的("x" = "X")。那么这就为true了?也不对,因为大小写是否敏感是基于数据库配置的,在当前的应用程序连接上特定的数据库之前,这个问题的答案都是不确定的。

那么我们可否选择使用String.Compare()来强制设置是否大小写敏感?这在Linq to Object中没问题,在Linq to Sql中就不行了,因为String.Compare()无法编译为SQL语句。因此,在Linq to Sql中,大小写是否敏感是一个依赖于环境配置的,这就提高了编码过程中由于疏忽而造成问题的概率。

为什么这样说呢?在以前,我们的C#代码和SQL代码是分开书写的,写C#的时候就很明确大小写敏感,写SQL的时候就很明确是数据库相关的。然而现在部分的SQL逻辑改为用C#来编写了,问题就出现了,特别是当你的代码中还混杂有Linq to Object的查询时,编写代码与阅读代码的过程中你一看到Linq就先要去想这段lamda表达式最终会被编译为哪种语言,MSIL还是SQL。如果你不进行这个区分,或者开小差把Linq to Sql的代码当作Linq to Object了,这就可能导致你编写了错误的代码,或者阅读上造成了错误的理解。

总体而言,虽然Linq to Sql为开发(特别是RAD)带来了巨大的便捷性,然而这种C#与SQL混合编写并且都使用C#语法的功能将会是一种先天的不足,它所带来的代码维护成本可能随着项目体积逐步增大而慢慢体现出来。至于将SQL混入C#所造成的分层模糊现象,我将在以后的文章中讨论,敬请关注:

Recruiting Events: Tencent (Part 1)

所有Recruiting Events系列文章由Cat Chen记录并发表于http://Chinese.CatChen.biz,目的不是公布招聘有关的题目或技巧,仅用于个人知识归纳以及经验总结,如需转载请保留出处。

我去参加了Tencent的技术笔试,发现无论你报什么技术职位,最后都考一样的内容,可能先根据基础能力筛选一下吧。因为题目受NDA保护,那就不说细节了,只能说学习好操作系统(特别是UNIX)、计算机网络、数据库、算法与数据结构这几门课程是非常有必要的。你需要对课本里那些死记硬背的知识有所掌握,例如各种排序算法的实质。我通常就只记得几种常用的排序算法,那些哪方面都没有明显优势的排序算法就忘记了,事实表明这些东西最好定期复习一下。另外你还必须对课程内容的实际应用有所了解,例如SSH是OSI模型中第几层的加密,UNIX中的pipe如何创建,等等。

Tencent的笔试中那些程序填空题,原来的程序显然不是高质量的、通过编译的代码,感觉就是手写一段代码然后去掉几句让你填空。首先是有陷阱,例如:
for (#expression; #expression; #expression);
  if (#expression) #blank
    else #blank;
看到这样的缩进,不要以为if...else...语句在循环之内,因为for后面已经有分号了,这样缩进写法分明是为了给你视觉错觉的。另外还有一题算法主函数没有return的,然而main()就调用该函数并打印返回值,这显然是代码没经实际测试就投入试题了。

反正考这样的题目,我的命中率又不行了。其实我很怀疑这些企业到底招什么人,是不是招一个Web的前端或后端开发人员,平时写写JavaScript或者PHP什么的,紧急时候要求你能够马上抽调去写C++或者做UNIX维护。

2007年10月28日星期日

S.T.A.L.K.E.R

最近沉迷于《S.T.A.L.K.E.R - 切尔诺贝利的阴影》这个游戏,名字上那几个字母是Scavengers, Trespassers, Adventurers, Loners, Killers, Explorers and Robbers的缩写。从游戏类型上来划分的话,这是个FPS,不过因为和NPC的可交互性很强,也可以当一个RPG(反正Fallout3也换成第一人称视觉了),至于其背景设定也有点点恐怖游戏的成份。

其实我以前对恐怖游戏没什么兴趣的,因为玩起来不爽,整体而言是游戏玩你的心理感觉,而不是你玩它。开修改器当然可以爽一会儿,但如果那是个单纯的恐怖游戏的话,这样子玩一会儿又很没意思了。不过我现在对非纯粹的恐怖游戏有些感觉了,其实就是让你在一定的心理压力下完成同样的竞争性任务,本质还是和游戏安排好的规则竞争。例如Manhunt吧,它刚刚发布的时候我觉得这么压抑一个游戏没什么好玩,不就是杀人嘛,还不能群殴,要想办法一个一个引诱出来杀,这实在太无聊了。不过自从浪费了不少时间在Hitman2与Hitman4中尝试各种古怪组合玩法后,我也开始向hardcore级玩家靠拢了,开始追求那些“有难度”的玩法。

这其实很符合The Game里面所说的,人类这种奇怪的动物,只要给他们一个有等级划分的游戏,并且告诉他们你花越多的时间在上面,你就能进入越高的等级,他们就会花时间在上面争取高等级。有很多玩家都是从悠闲型开始的,但一旦他们中了这个法则,就开始慢慢转入hardcore玩家的行列了。

另外值得一提的是S.T.A.L.K.E.R那个名为A-Life的全局AI系统。根据官方的介绍,由于A-Life的存在,游戏不会再像以前那些基于脚本的RPG那样,仅当玩家做了某事才会触发某个变化,而更像是Simcity那样即使你什么也不做后台的AI模拟过程仍然会继续发生,这包括军团之间的互相攻击以及地区的重新划分,还有异种的成批迁移,或者是袭击军团。游戏里面有1000个个性各异的NPC,由于整个大背景是在AI控制下模拟的,因此你碰到这些NPC的过程会是相对随机的。然而我还没第一次完成游戏然后开始第二次,因此我并不确认这个随机性以及模拟真实度有多大,不过从平时的游戏过程中看来,确实NPC门都是好像真有自己的目的而行动的,并非就永远存在于游戏中的特定位置等待你的到来并交给你一个任务。

这种全局的开放性交互从来都是游戏设计师的梦想,没有人喜欢在一个脚本制订好的游戏里玩两次,因此一部分设计师希望通过MMORPG达成梦想,而另外一部分继续在单机游戏的AI上钻研。MMORPG是完全开放的,但可控程度也不高,游戏规则只能先影响玩家群体,再间接影响到对单个玩家的直接游戏体验,而单机游戏的AI是可以直接控制的,游戏体验是可以通过调整AI而直接调整的。将来到底哪一个能够带来更好的游戏体验,暂时还很难说,不过至少我还是倾向于选择单机游戏。

2007年10月27日星期六

副业

什么是副业?在这里我们探讨两种副业。

第一种,也就是现在所谓的Web前端开发,也就是编写符合语义的XHTML,加上具备浏览器兼容性的CSS,然后还有负责AJAX通讯以及DOM操作的JavaScript。在以前是没有前端开发这个说法的,因为这就是副业活,负责前端开发的,其主业通常是服务器端开发,又或者是图像设计,然后就兼职写写上述三种代码。正因如此,前端开发相关的这三种技术一直发展缓慢,因为人人都当它是副业,没必要也没时间去深究。例如JavaScript,其实它很早就支持我们现在所使用的Prototype中的各种功能,为什么当初就没有人去开发Prototype呢?首要问题就是没人觉得有必要去挖掘这些技术,就算有少数人挖掘到那个深度了,共享出来的知识其他人也不会去吸收——谁会了为副业而研读一大堆相关资料呢?因此,直到最近AJAX的热门,才导致了更多的人把前端开发当作主业来看待,也才突然有了那么多相关的技术讨论与书籍出版。

第二种,是中国学生所谓的研究与探索能力。有人说,中国学生的动手能力并不比美国学生差,其实这估计是基于双方都尽自己能力去做的前提下得到的机轮。不比别人差,并不代表着有同等的动机以及动力去做。就如前面说JavaScript是副业的例子,中国学生的真正缺失在于把动手研究与探索当作副业,是在应付考试这一主业得以游刃自如应付后才考虑的事情,同时并不会投入大量的精力关注有关的信息以及细节。

深入的就不探讨了,不过仅仅做一个小提示——你是否有留意到你生活中的某一样副业其实是极具潜力的,如果你不是很讨厌做那件事情的话,不妨考虑一下更换工作主题的时候将它提升上来作为主业,或许因为竞争对手少反而更容易成功。

2007年10月21日星期日

China MVP Open Day

在两日三天的海南三亚MVP峰会之后,终于回到了广州。MVP峰会的最大好处就是能见到来自各地的MVP,并且是面对面地交流,这真的比隔着一个Windows Live Messenger要好很多。另外三亚虽然不算是个一流享受的地方,不过看起来也还算很美。

我和另外两位广东的MVP,也就是彭斌黎波,都是在18号飞往三亚的,因为飞机1135起飞,所以我们约定了到了三亚进酒店之后再吃午餐。到了酒店之后,发现原来北京的MVP也到了,并且已经在餐厅吃饭了,就加入他们了。吃晚饭休息了一会儿,我们就到游泳池泡水兼聊天,我基本上就是跟着彭斌周围去认识别的MVP,不知不觉就在水里泡了n个钟。

晚餐前我跟着彭斌去找葛涵涛,发觉他们房间里的Windows Mobile设备还真多啊。之后俞晖也来了,拿着一个iPhone,我终于有机会亲眼看看iPhone那个屏幕了,并且体验了一下操作起来的手感,更加下决心要买个iPod touch。晚餐还是跟着北京的MVP,这次是去酒店外面的大排档吃海鲜,发现北京的MVP真是团结,总是一起行动。晚餐的海鲜感觉一般般啦,反正我不觉得哪一样做得特别鲜味的。晚餐过程中比较享受的是黎波的歌声,当然还包括现场高涨的fans情绪,哈哈。另外俞晖的iPhone在葛涵涛带领的Windows Mobile MVP群体攻击下,成为了“不具备企业应用能力”的反面例子。

晚餐回来后,还是泡水,哈哈……然后是跟彭斌、俞晖一起聊天,说到了博客园的发展,然后又说到了博客园最近上线的digg功能,另外还有一些各地.NET俱乐部发展的事情。我觉得这样的面对面聊天比在网上打字要轻松多了,虽然在线也可以音频会议,但要比划什么还是不如面对面的方便。我们聊天聊到一点多才睡,第二天要起来参加颁奖啊,睡眠不足啊……

19号早上是集体会议,包括Karen-Anne Young(APAC与EMEA地区Group Manager)的keynote,以及随后一些受NDA保护的内容,那些就不方便说了。在keynote之后,Karen亲自为我们每一位参加峰会的MVP颁奖,并且合影留念。19号下午是分组会议,Dev、IW和IT分开,我当然是去听Dev那场啦,内容包括Silverlight、Linq以及Windows Embedded。

晚餐是在游泳池边举行的自助餐,算是个小party吧,其中包括Karen的dancing哦!然后是问答环节,考察大家对微软与会员工的熟悉程度。接下来各位员工还各自出来自我介绍了一下,顺便还介绍了一下他们当前负责的社区项目。晚餐之后还是聊天时间,这次我和彭斌找到了陈荣陈黎夫,变成了ASP.NET MVP小聚了,可惜就缺了赵劼。回到房间后,已经十一点多了,王兴明过来吹水,又吹到了一点半。在这种聚会上面,你是不会愿意浪费时间在睡觉上的,因为有那么多有技术又能说的人在你身边,你总是想不停的找人聊天。

20号早上是沙滩活动,首先80多人一起在海滩上排一个MVP字样,让摄影师帮我们拍照,然后是小组比赛。之后有人踢足球,有人打排球,我就跟着彭斌去拣贝壳。拣贝壳这事情还挺考耐心与敏捷的,海水一冲上来你就要盯住有没有值得你捡的东西,有的话就要马上按住,否则水一退就又冲走了。顺着沙滩走过去,发现有不少外国人,哎……这里看来成美元区了,人家都在这里花美元,物价那么高,花人民币就感觉心痛。

下午我们一群MVP自发组织去吴支洲岛,逛了一个圈回来,其实也没什么特别的吧,就是不停的拍照拍照。这个自发组织啊,当然就不如微软出钱会务公司安排好那么体贴,有时候安排挺混乱的,有些人想这样又有些人想那样,团队管理问题啊……大多数MVP在工作中还真烦这个问题呢。从岛上回来后,我们直接进市区,吃了一顿麦当劳,买了一点特产,就打的去机场的。领票之后,我还傻傻的问11号门在哪里进,对方告诉我先进安检,我才想起这不是白云机场,不管你进几号门,都是一个安检通道。原本2115的飞机,延迟到2235,结果回到家已经凌晨了。

整次旅行的照片我会陆续挑选一些好看的发到facebook上面去,有兴趣的可以去看看哦。大家如果觉得有话题在三亚没讨论完的,可以在Messenger上继续讨论。

2007年10月15日星期一

Recruiting Events: Xunlei (Part 2)

所有Recruiting Events系列文章由Cat Chen记录并发表于http://Chinese.CatChen.biz,目的不是公布招聘有关的题目或技巧,仅用于个人知识归纳以及经验总结,如需转载请保留出处。

前端工程师在广州的笔试复试只剩下4个人,我不明白为什么4个人都还要再笔试,如果前端技术的基础算是考察了,那么应该可以面试考一下设计思路以及综合能力,难道还要再笔试一次考更深入的技术吗?如果是那样的话,我觉得没有谁能做出来,因为即使第一次笔试那5道题,我问了猫影aw,他们也无法在不查询外部资料的情况下做出来(不过aw应该对JavaScript和ActionScript互调用那题很有体会,所以那题的答案应该找他讨论)。

发卷的时候,考官对我们这4个人说了一句“今天考一考基础”,然后看到试卷类别上写着C++,我就不知道说什么好了。C++我会读也会写,只不过基于我个人对C++语言本质的不太喜欢,所以也就不会think in C++,因此写出来的代码显然很不像C++ native speaker,并且会带上其他各种语言的一些accent。看看试卷,50道单选,20道填空,1道编程,有人要考基础我就跟他拼基础吧,只能这样了。

开头有几道选择题挺开放的,当也可能是纯粹考你最基础的基础。例如问题是以下哪个你看起来更顺眼,有一组答案中有两个分别是:
#define your_name "xunlei"
#define YOURNAME "xunlei"
这算是考什么呢?生硬的考他认为正确的命名规则呢,还是仅仅想了解你个人的编码风格呢?后面开始有一些题目考你对C++各种不一定人人用得着的功能,例如以下两个是否有性能差别:
#include<iostream>
#include "iostream.h"
这个……简直就犹如当年算法竞赛能够在初赛考你Pascal里面哪一个编译控制指令能改变堆栈大小一样,结果就是只有两类人能够做得出来:超级熟练的使用者,或者死记硬背的应试者。

填空题的话,也是有点像竞赛的笔试题,例如叫你看程序写运行结果,又或者补充程序代码中的空缺。ACM都直接上机考啦,C++还以这样的形式考察,不要说招前端工程师,就算真的是招C++开发人员,考察结果与实际能力只见也会有较大的误差吧。

最后的编程题,要求写一个字定义的strcpy函数,签名如下:
int strcpy(char* dest, char* source, int count)
其中count控制最大的复制字节数,返回值为实际复制了的字节数。题目不算难,就照我自己的思路写咯。后来Jeffrey Zhao才告诉我,这不正是strncpy吗?可惜strncpy的返回值不同,所以还是不能直接调用。

我还是100分钟左右就做晚了,然后又花了20分钟慢慢检查了一遍,修改了一些选项,其实改来改去都是不确认哪个是对的。之后等了一个晚上都没有面试通知,就知道出事了……

回过头来分析一下,到底什么人比较容易通过这样的两轮笔试呢?要熟记正则表达式,知道document.cookie存储格式,知道C++编译器的控制选项,这些都要求是超级熟练的使用者,或者死记硬背的应试者。我想没有任何企业想要后者,那么只能说迅雷想招前者,看重的是你是否是一个熟练的IT民工(来自于猫影词汇),是否上任后能马上投入工作。但如果这真的就是迅雷的招聘风格的话,我只能说和它招聘网站上宣传的相差太远的。网站上的宣传,让它看起来好像一家小百度那样,拥有大企业应该有福利,至少达到百度那个级别(当然宣传往往是比真是要夸张一些的,我说的百度级别是真实级别),同时又作为新兴企业拥有无限前景——这主要是指股票那事儿。然而事实上,可能它仅仅是有一家很中国式的小企业,就是作为整个大的世界工厂里的一个小工厂,依赖自身的光环招一些有理想的工人进去,然后……谁知道将来发生什么事情呢,这可是说不定的。

2007年10月13日星期六

Recruiting Events: Xunlei (Part 1)

所有Recruiting Events系列文章由Cat Chen记录并发表于http://Chinese.CatChen.biz,目的不是公布招聘有关的题目或技巧,仅用于个人知识归纳以及经验总结,如需转载请保留出处。

第一次参加企业招聘笔试,竟然是迅雷……嗯……其实也没什么好“竟然”的,应该“竟然”的是“第一次”,完全没经验的情况下就跑去一所“陌生”的学校和一群陌生的人考试,而且课室里大部分人还不是和我考一样的题目。

我仅仅应聘一个职位,就是前端开发工程师,因此找到对应的课室就坐下来。之前幸运的碰到一位同班同学,他说简历投了三个职位,分别有短信通知他到三个不同的课室笔试,不知道他后来怎么办了。前端开发工程师的题目就5道大题,统一的答题纸上钩选应聘职位的竟然没有“前端开发”一栏,faint死了……

第1题考正则表达式,一个小题要求匹配字符串中首尾的空格,另一个小题要求匹配仅包含数字、字母、下划线的6到20位的字符串。我不熟悉正则表达式,所有东西都仅仅有个概念但记不住,想了很久也没写什么,最后决定凭直接写。第一题想不到空格是斜杠什么,空白字符好像是blank吧,于是写了个(\b)*.*?(\b)*上去,其实空白字符是space,应该是(\s)*.*?(\s)*,汗……中间那个.*?是非贪婪匹配,否则把末尾的空白自己也吃掉了,我只记得非贪来匹配好像是*后面加一个?,然后就写上去了。第二题我同样不知道[A-Za-z0-9_]其实就是\w,于是就写了[A-Za-z0-9_]{6,20}。

第2题要求写一个String类的扩展函数,要求按老ASCII码单字节/汉字双字节模式,计算一个字符串的字节长度。这个……我又载晕掉了,JavaScript中的String能够好像传统语言的char[]那样使用索引号获取单个字符,但是JavaScript没有带表单个字符的数据类型,获取到的还是字符串,两个字符串之间能够用大于号小于号比较的吗?在JavaScript中我还真的没这样用过呢,不过某些高级语言支持字符串的大小比较,结果相当于按字典排序后两个字符串的顺序比较,估计JavaScript也支持,所以我就按照那样子写了。答案如下:
String.prototype.byteLenght = function() {
  var count = 0;
  for (var i = 0; i < this.length; i++) {
    if (this[i] <= "\u00ff") { /*"\u00ff"是单字节ASCII码中最大的一个*/
      count = count + 1;
    } else {
      count = count + 2;
    }
  }
  return count;
}
在做完整份题目后,我又无无聊聊在代码后再加了一段注释,大意是:在JavaScript中实现这样的计算完全没有实际用途。首先,因为JavaScript是完全基于unicode的,按ASCII方式计算字节长度没有意义。其次,如果这时用于计算屏幕显示宽度的,其实也不准确,在非等宽字体中"w"和"i"的宽度差了几倍,显示宽度不能用这样的方法计算。

第3题是要求列举所知道的JavaScript与Flash中的ActionScript互调用方法,还要求提供代码说明。我仅仅在过去有过一点点了解,就是Flash的ActiveX对象对外暴露两个方法,分别用于传入和传出一个字符串,而这两个方法对应调用内部的ActionScript函数,因此ActionScript的处理应该写在这两个函数之中。当时就这样写上去了,并且说明我不记得具体的函数名称,给我查资料或者上网Google的话就会知道。我不知道阅卷那个人看到我每题都一堆回答以外的评论会怎么想,哈哈……后来查了一下,我印象中的那个函数其实是远古的fscommand,而Adobe现在建议大家使用ExternalInterface。

第4题要求列举cookie,并且包括过期时间。同样是不懂,记忆中只有document.cookie一样东西,从来没调用过,仅仅阅读过一次别人的调用代码,按照什么格式把相关属性保存在String我也不清楚,就知道第一个是键值对,至于过期日期……真的不知道了。既然连按照什么格式读取都不知道,所以这题我干脆就什么都没写,本来想着不会编写代码也能写写我所知道的,后来决定跳过。其实知道格式就很简单了,格式可以参考quirksmode上讲述cookie的文章

第5题是开放式的,要说自己擅长的前端开发方向。我用了一版纸回答前面的3个问题,又用了一版纸完成了第5题的吹水,哈哈……说了擅长XHTML + CSS + JavaScript,讲了一下过往项目,最后不忘插上一句“更多项目信息请浏览CatChen.biz”。

两个小时的笔试时间,我一百分钟做完,没有一道题是完全懂并且有把握的,因为我是习惯有什么不懂就去搜索的人,从来不背,而考到的点都是我平常很少用的。考完半天后就收到笔试复试通知了,接着第二天去复试。

2007年10月10日星期三

不要动态修改 DOM 里面的 id 属性

我不知道是否有什么标准规定不允许修改id属性,或者不建议修改id属性,总之IE对此支持不佳。在IE当中,修改id属性对将来的DOM操作没什么不符合标准的影响,你仍然可以通过document.getElementById()找到该元素,然后在CSS支持上就有问题了,id修改后并不会自动去重新匹配CSS规则,因此id修改后不会体现出应有的样式。

因此,进行DOM编程时应该避免修改id属性,如果需要动态改变匹配的CSS规则,就改变class属性吧。我碰到这个问题时也就是尝试通过修改id属性修改唯一匹配某个CSS过滤器的元素,结果发现修改后在IE中完全没有修改,之后改为用class属性实现同样的目的。

2007年10月9日星期二

.NET Framework 开放源代码

一些.NET Framework的源代码开放了,基于MS-RL许可,并提供调试整合到VS2008当中了。从旁观者的角度来说,这是Microsoft迈向开放与社区化合作的一大步,很多人也把这当作历史性事件,然而对于一般的开发者而言呢?这事情到底有多大影响力呢?我认为对于开发者来说,不同角色的开发者遭受的影响是不同的,并且整体影响是导致分工继续细化。

.NET最内层的本质是什么?Microsoft曾经非常引以为豪的COM,.NET只是这种思想一路实践并且进化而来的结果。.NET最开始设计为满足RAD的需求,以便吸引使用其他语言、框架的程序员转移过来,然而开放源代码后RAD的程序员仍然是RAD的,这对他们几乎没有任何影响。想象你是一个习惯于拖放一切的ASP.NET开发者,基本上不想写任何业务逻辑之外的代码,数据访问层用Typed DataSet或者Linq to Sql搞定,界面用现成的Control和Extender,Microsoft这次提供的源代码对你有什么意义吗?因为你不需要自己编写Control或者Extender,自然你不会花时间去了解有关的模式,也无须查看内置控件的代码。如果你调用内置控件出问题了,在Google以及调试内置控件之间,你显然会选择前者。因此,对于习惯于RAD的程序员来说,开放源代码这件事是没有任何直接影响的。

然而,有些间接影响是不能忽略的。前面提到了使用Google搜索问题的解决方案,然而Google自身并不懂得解决问题,答案其实来自与其他已经把问题解决了的程序员,因此这些源代码如果确实帮助了其他类型的程序员解决了问题,那么也就间接帮助了RAD程序员。

那么还有哪些类型的程序员呢?例如做稍微底层一些工作的,编写Control、Extender、HttpHandler、HttpModule等可复用组件以便为自己或别人提供方便的。编写可复用组件最糟糕的地方就在于它是可复用的——你永远不知道别人会将它以什么样的方式用在什么样的环境,因此按照一定的模式开发这些组件以便保证兼容性就很有必要,而模式本身最好就参考自.NET Framework内置的同类组件,除非你想更大范围地研究.NET Framework并重新发明轮子。因此研究与模仿内置组件的行为是组件开发者的必修课,而从ScottGu文章(Releasing the Source Code for the .NET Framework Libraries)中的截图看来,内置组件丰富的注释将有助于程序员更轻松地理解其原本的设计方式,从而更轻松地在自己的组件中模仿内置组件的行为。事实上,有很多内置组件是设计为对另外一些内置组件特别照顾的,这类型的耦合在Reflector中阅读代码时是最难以理解的,如果阅读有注释的代码相信会轻松不少。

最后,开放源代码可能将会导致对.NET Framework进行纯粹思想或理论作研究的人数增加。事实上,无论.NET Framework多么倾向于实用型,如果Microsoft需要获取来自社区的创新思想,还是必须吸引一群思想家的,否则大多数的社区创新都只是应用与应用方法,Microsoft还是独揽.NET Framework前进方向的控制权。这种中央集权有它高效的地方,特别是发展初期,Microsoft能够根据自己的实力战略性地安排新特性的研发顺序。然而Microsoft也曾经因此吃亏,例如ASP.NET 2.0没能引入AJAX支持,直到最后才急忙补上一个Callback特性,并承诺日后开发完整的AJAX库。因此,倾听来自社区的观点很重要,而要求社区有观点就必须先提供素材给他们讨论,开放源代码将能够激发社区对.NET Framework的研究热情并且提供更多能够作为反馈信息的新观点。

因此,就.NET Framework开放源代码这样一件事情而言,对于不同的开发者其影响的大小是不同的。同时我们也能预期Microsoft本身肯定也是最大的受惠者之一,否则以其智慧绝对不会做这样一个决策。

2007年10月6日星期六

Is following spamming?

一般来说,信息发布总有主动的发送方和主动的接收方,如果spammer是主动发送方,你可以积极拒绝;然而如果spammer是被动发送方,那么你可能就不容易留意到了。

从最普通的email spamming开始说吧,这完全是spammer主动发送给你的,因此你可以用过滤器过滤掉,或者看到标题就直接不看了。然后发展到论坛上的spamming,明明论坛是公共场所,大家都是平等的,但有些人就发一些spam上去,因此论坛上的信息对你来说不再是平等的,有些你一看到就会绕开。之后还有blog spamming,blog是你的,而spammer本应是被动的接受方,结果他却成了被动的发送方,每次你有新文章他就上来跟一个spam,因为你的文章是主导,所以spam变得不是那么不显眼。最后我要说的,就是别人在Twitter上follow你算不算一种spamming。

我昨天突然收到一个following通知,follower的名字是我不认识的,所以我就去看看那个人自己发什么信息,然后发现那个用户根本就没什么自己直接发布的信息,都是那种直接将自己blog的feed转发到Twitter并附上TinyURL的。然后我就想,他是不是再spamming我呢?至少如果他是spammer,那么这种做法是成功的。例如你写一个blog,你想让别人订阅你的feed吧,推广可不是容易做的事情。然而你把feed发布到Twitter,然后follow一堆陌生人,首先这些陌生人绝大部分都会因为好奇而看看你的Twitter,那也就相当于看了你的标题列表了,如果你的blog主题对他们有吸引了,这就成功吸引他们订阅了。就算不是这样,很多人也会出于“你认识我然后我又认识一下你吧”这一心态而在Twitter上follow你,那其实也就是订阅了你的feed,这样你也算成功了。

最后,我想说的还是一个观点,Internet是不需要直接实事实名制的,在用户使用Internet的过程中会逐渐发展出一套适用于Internet的信任与安全机制,也就是说每个人都粗略懂得在Internet上什么人能够信任什么事情是安全的。另外提供一篇相关阅读:使用实名SNS网站(校内、Facebook)请注意安全

2007年10月3日星期三

Leeching and Seeding

最近用TorrentLeech下载了一些东西,感觉比在TLF下载BTTEAM发布的还要爽,然而却只敢选择性的下载一些想要快速下载的东西,不敢说看到什么就立即想下。

TLF BT,几乎所有下载都是seeders明显小于peers,也就是说包括BTTEAM的发布者在内,只有少数几个人在seeding,其他多数人都是exchanging的,也就是说既download也upload,这也就是我们理解中P2P network的样子。然而大多数下载者都是下载完就关闭BT的了,完全不关心自己的上传下载比率(ratio),因为也没有理由去关心,反正总有人在那里seeding的,等seeders自然减少到零了一个torrent就逐渐过期失效了。

然而在TorrentLeech就不一样,除非是刚刚发布的torrent,或者非常冷门的torrent,其他基本上是seeders大于leechers。seeder大家都能理解,就是初始上传或者下载完了完全上传不下载的人。那么leecher是指什么呢?leecher是指完全下载的人,在leeching的过程中没有任何的义务,你可以什么都不干就享受高速下载。基于seeders大于leechers的事实,leeching的速度非常高,有很多个seeds抢着给你送数据。然而面对如此高速的leeching,却不是想下载什么就随便下载的,因为你必须很清楚之后才需要付出的代价。

正如TLF的新手指南所说,在国外是没有免费午餐的,你要下载就必须要有付出,就算你本身不参与任何协助0day发布的工作,至少你要在P2P network里面上传,上传下载比率为1.0是基本的要求。例如TorrentLeech,新注册用户的前4G下载是暂不计算ratio的,下载超过4G之后系统就会开始检测你的ratio是否达到最低要求的0.4,达不到的话就出warning。别以为warning好像口头警告那样随便,warning相当于黄牌,下一步就是ban出局,你想重新获取邀请注册TorrentLeech就很难了,并且邀请你的人可能也要负责部分责任。如果你的ratio在0.4到1.0之间,TorrentLeech的下载等候与下载并发控制机制会激励你尽量提高自己的ratio,最开始任何新增下载你都必须等48小时才能开始下载,并且同一时间你只能下载一个torrent的内容,然而随着你的ratio和总上传字节数提升,最终可以达到无需等待以及无限并发下载。

可能这也就是中国和美国之间差异的一种体现。在中国你可以不贡献,你名义上是exchange,但实际上就是真的在leech,并且不用遭受任何惩罚,于是所有人都在想尽办法的拼命抢夺稀有资源。在美国的话,你可以对政府有信心,你可以相信你需要的资源是补给充足的,没必要以各种恶劣手法去获取,然而你必须承担相应的责任,逃避义务的后果是非常严重的。而且由于公平对待也就不存在杀鸡儆猴的做法,你不能说因为你不是违章中比较突出的那个就抱有侥幸心理。

最后,补充一些纯技术上的解释。习惯使用公开型torrent tracker站点的人,可能不理解TorrentLeech是如何统计ratio,P2P嘛,我和别人交换了多少数据你怎么知道,我下载时你又如何得知我是哪个用户。TorrentLeech的做法是在torrent文件里面嵌入你的帐号指纹,之后你的客户端一旦联系tracker它就知道你是哪个用户了,从而控制你的等待时间以及并发下载。同时所有torrent都是禁止DHT等用户间直接数据交换的,所有数据流由tracker引导,这就可以正确统计数据了。你强行要开DHT的话,通过DHT上传的流量不计算入总数,而且如果其他人没有开DHT的话你就没有DHT下载优势。

2007年9月20日星期四

Binary Choice & reCAPTCHA

有些时候我们面临binary choice,也就是二元选择,的时候我们会认定只能选一个,因此必须牺牲另外一个,如果无法明确区分两者轻重,这将是一个极为痛苦的选择。不过我最近在读李中莹的《重塑心灵》时发现,原来我们应该积极去考虑如何能够两全其美,而不是被迫接受binary choice。

举个例子,为了防止有人使用bot连续请求某些web资源,我们可能引入CAPTCHA,也就是通常所见的验证码,以确认请求是真实的。通常我们也就默认了,越高级别的安全要求,就必须付出越多的代价,然而一个叫做reCAPTCHA的服务却不这样认为,至少它认为为提高安全性所付出的性能代价能做点别的有益的事情。

如果一般的验证码一样,reCAPTCHA也是提供一组处理过的英文字母,要求用户识别并输入。不过reCAPTCHA并不直接以零售模块形式嵌入你的站点,而以web2.0流行的API方式免费提供,你可以使用它的API为你的站点中需要的位置加入验证码。可能你会问,reCAPTCHA这样做有什么好处,因为它提高了你的站点安全性,却自己付出了性能代价,难道它在验证码下面显示一段广告?嗯……这是常人的思维,确实有很多web2.0服务通过附带广告盈利,不过reCAPTCHA可是Carnegie Mellon University的项目,所以不可能有广告的。一所大学完全出于计算机安全的研究提供免费的CAPTCHA服务?也不是,其实Carnegie Mellon University在大量扫描书籍,同时利用CAPTCHA提交者做人肉OCR。这时候就想通了吧,CAPTCHA虽然占用了计算机资源的同时,也占用了人的大脑资源,然而reCAPTCHA这个项目成功的让CAPTCHA提交过程中的资源浪费变成有效的资源消费。

根据reCAPTCHA的介绍,每天有六千万个CAPTCHA被人们识别,其中每一个大概消耗10秒钟时间,然而将这些工作时间聚集起来就非常多了。那么reCAPTCHA到底是如何工作的呢?当后台的扫描服务遇到一个无法识别的单词时,它就会和一个真正的CAPTCHA单词并列提供给用户输入,用户输入之后就仅对比真正的CAPTCHA单词,如果对了就当他输入对了,同时认为他输入的另外一个单词就是识别结果。对于一个无法识别的单词进行多次这样的操作,就能得到一个具备高确信度的结果。

如果你希望帮助该项目识别更多的信息,你可以在自己的网站上放置reCAPTCHA。对于WordPress和MediaWiki都有现成的reCAPTCHA插件,对于PHP、Python、Perl、Ruby等动态语言也能轻松找到reCAPTCHA库。另外reCAPTCHA还有一个MailHide的子项目,能够让你隐藏你的email,用户必须点击链接并输入CAPTCHA后才能看到完整的email地址。没错,就和Google Groups上面的效果一样,不过CAPTCHA用的是reCAPTCHA,并且也提供一堆现成的插件与库,有兴趣的可以去官方网站看看API文档。

2007年9月9日星期日

Minimal Blogging and Micro-Blogging

最近大家都在说micro-blogging,所以我也来说几句。为什么国内开始多人讨论micro-blogging了呢?估计是因为饭否在国内已经热了,能够有所谓的“现象(phenomenon)”了,同时国内其他Twitter的copy cat之间的竞争也越来越激烈了。然而我却一直用Twitter,无论身边都少人用饭否都不转过去。首先因为我比较无视copy cat,对我这样觉得原创很重要的人来说,copy cat服务通常看都不看一眼,知道是那么一回事就是了。每天有什么新的web2.0 startup出现,订阅TechCrunchRead/WriteWeb这样的web2.0观察日志就够了,基本上各种古怪的创意尽收眼底,一个copy cat根本不值得我花时间去看。一个新服务对比其参考服务至少要有自己的创意以及更好的市场定位,我才觉得有必要去了解,否则每天新增的web2.0 startup那么多我怎么可能了解得来。

回到micro-blogging的话题来,事实上我觉得Twitter这样的可以叫做minimal blogging了。minimal blogging这个想法来自minimal design,因为当你第一眼看到Twitter的时候,你肯定会说那是一个minimal design的。所谓的minimal design,也就是最小化设计,历史上曾经有一些人指责CSS仅能实现最小化设计,也就是只能由一些矩形的区域组成整个页面(参考Minimalism),更复杂的设计css就处理不好。而Twitter则被我称之为minimal blogging,因为它只能处理最小的最有限的信息,一篇最多就140个字符,不允许链接,不能是HTML或者别的markup language(例如ubb code)。虽然我一直在用Twitter,甚至还测试过从中国电信发一条短信给Twitter的英国接收号码要¥1,然而其实我并不觉得Twitter对我很有吸引力。一方面,我并不喜欢被flooding的感觉;另一方面,我想要的是micro-blogging不是这么minimal的。

对我而言,micro-blogging意味着我不需要想太多的就能发表,而且发表过程操作简单,然而同时要能够分享我能接触的各种信息。Twitter符合了前面两点,但是不符合最后一点,因为它无法发链接和图片,视频或者音频就更不要说了,不过因为我不是视频和音频爱好者所以这两项就免了。暂时而言,我使用Opera Community进行mobile blogging,也就是Cat in Mobile。它允许我在手机上即拍即发布,文字和照片都有了,对我而言就差一个链接了,不过mobile blogging不需要链接,要链接就通过feed和我的del.icio.us聚合一下好了。至于不需要照片的场合,Twitter也就将就着用了,因为无论如何同样是手机上的Opera Mini,Twitter操作起来还是比Opera Community要方便的,要知道手机上多填写一个表单就增加不少操作复杂度。

我真正向往的是Tumblr,一个tumblelog服务,然而我却从来都不使用它,最近也就仅仅导入了几个feed进去。其实这是很正常的事情,就好像除去Macbook之后VAIO真的异常吸引,然而即使我有钱都不会买VAIO,首先因为它确实烧钱,其次是日本产品的保修服务根本就不和欧美的同一个等级,意味着它的维护成本超高。日本产品,大至汽车小至PDA,停产之后都不会留充足的备件,如果你买的产品在保但是已经没备件了,那就无法修,因此我非常不喜欢购买日本产品,不用考虑保修直接吃进肚子里的除外。Tumblr看起来拥有我想要的功能,同时又保持micro,然而对我而言迁移成本实在高。在使用Twitter之前,我没有micro-blogging的需求,所以当时也没思考我能在我的Tumblr上发什么,然而在写这篇文章的同时,我在极力挖掘Tumblr的潜力,以及思考低成本的迁移方法,或者有一天我就迁移到Tumblr了。

最后一句话,为什么我向往Tumblr?因为国外的web2.0达人们有不少在都在用,例如Digg的共创人之一Kevin Ross

深入理解 ASP.NET 动态控件 (Part 4 - 解决问题)

前言

在开始写这个系列的文章之时,我想着必须深入介绍背后的原理,然后将所有需要的背景知识呈现到读者眼前,不过我现在发觉这并不是好的写作方法,要写下去对我自己来说难度也不少。最近受到Infinities Loop发布TRULY Understanding Dynamic Controls (Part 4)的刺激,我决定继续写这个系列的文章,并且领悟到了更多读者需要的是对问题的一种较为易于理解的解释,而非一种严谨的解释,因为前者更有助于读者解决当前问题并在再次遇到类似问题时自行推导解决。

其实我在本系列的第一篇文章就已经明确了怎样的文章才让读者容易接受,现在是我自己误入歧途了,所以必须纠正过来。第一篇文章的结尾建议读者自己阅读控件开发有关的书籍,之后就能完整理解和解决这个问题。实际上这是一个比较不合理的建议,大多数人并不可能花时间看完一本厚厚的控件开发书籍(ASP.NET 2.0的比ASP.NET 1.x的要厚了不少)。我需要做的不是复述书上的观点,那也不是我想要做的事情,真正需要做的事情是将书里面的观点浓缩为一篇文章,要让读者能解决问题的,推理看起来符合常识以至于容易接受,然而又比严密推理省下一大多文字。好吧,就让我们按照这种思路去看看如何解决一些常见的动态控件问题。

问题分类

这是受到Infinities Loop启发的,我们首先要将问题分类,然后逐个击破。这里的分类将最常见也最容易解决的排到上面来,然后逐步深入讨论。在开发过程中,使ASP.NET程序员想到要用动态控件的情景通常有如下几种:

  1. 你需要呈现不确定数量的控件,但这些控件是同一类型的
  2. 你需要呈现不确定类型的控件
  3. 上述两个问题的综合或嵌套
  4. 你需要开发自己的Web控件

在对问题进行分类之后,我们就容易逐个去分析解决办法了。由于分类是按照难度逐步递增的,所以这对读者来说应该是较容易理解的。

不确定数量的同类型控件

如果你要在页面上显示一个调查问卷,问卷的题目来自数据库,而任何问题都只有“是”与“否”两个选项,你决定使用RadioButton提供选项。这时候动态创建控件的念头应该仅仅是一闪而过的,然后你就决定使用Repeater。

如果你在这时候没有想到Repeater,或者任何的TemplateControl,那么你就需要重新熟悉ASP.NET的内置控件了。很多时候我们用多了GridView,特别是一直都用BoundField的话,就很容易忘记世界上还有TemplateField这么一回事。

那么为什么Repeater在此会是一个好的选择呢?首先,连最近基本的foreach迭代循环它也帮我们做了,我们仅需要指定DataSource,然后执行一下DataBind(),它就帮我们动态为每一个数据项按照模板创建控件。其次,对于PostBack之后数据发生更新的情况它能应付自如。为了说明PostBack更新数据造成的影响,让我们再来看一个例子。

首先,我们直接在Session里存放一个string[],内容为{"apple", "boy", "cat", "dog"},然后我们需要将它们显示出来,每一个项目显示为一个LinkButton,点击之后就在数组中将它删除。我们都知道使用Repeater或者GridView搭配ObjectDataSource做这样简单的事情是绝对没问题的,但如果我们手动编写动态创建控件的过程呢?

按照大多数人所理解的ASP.NET逻辑,首先应该在Load这一阶段遍历数组,然后为每一个数据项创建一个LinkButton,最后把这一切都附加到页面上唯一的那个HtmlForm上面去。删除怎么做呢?LinkButton实现了IButtonControl,所以可以添加CommandArgument属性,我们就把字符串保存进去好了。在OnCommand的时候就通过此属性识别当前需要删除的字符串,然后从数组中删除,并且还要在HtmlForm中搜索对应的LinkButton然后把它移除。

这时候你应该看看OnLoad中的代码是否记得为每一个控件的ID属性赋值,否则就会出问题了。页面一开始生成的结构应该是这样的:(左侧的是控件的ID,右侧是控件显示的字符串)

ctl01 ("apple")
ctl02 ("boy")
ctl03 ("cat")
ctl04 ("dog")

我们点击"boy",页面进行PostBack,然后Load生成同样的控件树,之后OnDelete删除ctl02,所以输出的控件树应该是这样的:

ctl01 ("apple")
ctl03 ("cat")
ctl04 ("dog")

我们这次点击"cat",页面又在PostBack,但接着Load生成的控件树就不同了:

ctl01 ("apple")
ctl02 ("cat")
ctl03 ("dog")

必须留意到控件的ID属性重新编号了,然而ASP.NET仅仅知道我们点击了ctl03,所以触发的ctl03的OnCommand,根据现在的ctl03的CommandArgument属性,删除了"dog"字符串。这就是所谓的问题了,无指定ID的控件会自动按顺序分配ID,因此ID具有了不确定性。

如果在OnCommand的时候,调用HtmlForm的Controls.Clear(),是否就能移除所有控件并且让ID重头开始编号呢?实验结果表明上述删除过程中第一次PostBack后会生成这样的控件树:

ctl05 ("apple")
ctl06 ("cat")
ctl07 ("dog")

也就是说,移除确实是移除了,然而ID编号没有重置,而是继续编号。那么Repeater是怎么做到的呢?为什么直接使用Repeater就没有任何问题呢?这个下一篇文章再说,我们现在专心来把问题逐个击破,现在你记住这种情况选择Repeater或者其他更高级的数据控件就是了。

不确定类型的控件

在面对此类问题的时候,首先问问自己控件的数量,如果数量不多,直接通过设置控件的Visible属性解决问题就是了。这也就是说,把可能要显示的控件都声明为Visible="false",然后在代码中判断当前应该将哪个显示出来。

如果控件比较多,然而还是能分组的,同一时间仅仅显示其中的一组,那么你应该考虑使用MultiView,这样你的工作将会轻松不少。事实上,能够使用MultiView解决的,都应该优先考虑使用MultiView解决,这比起自己控制哪一个控件显示哪一个控件隐藏要方便多了。其实MultiView所做的,也就是帮你控制控件的显示与隐藏。

这样做的性能如何呢?我们关注两方面的问题,一方面是服务器端执行的资源消耗,另一方面是传输的带宽消耗。我们先来看看服务器端执行的资源消耗吧,我们最常见的消耗应该就是数据控件操作数据库时的消耗了。在ASP.NET 1.x时代,我们没有数据源控件,所以必须手动进行DataBind(),这也就是说如果不手动执行DataBind()的话就不会进行任何数据操作,因此只要我们记得在数据控件不显示的时候也不要让它执行DataBind()就是了,那样就不会有性能损失。在ASP.NET 2.0当中,使用数据源控件的话数据控件是会自动DataBind()的,这时候会造成控件隐藏时的资源消耗呢?事实上是不会的,数据控件即使已经定义了DataSourceID属性,它也仅仅在自己第一次可见时才进行自动DataBind()。如果数据控件的状态是隐藏的(包括使用MultiView隐藏),它就不会自动进行DataBind()。因此,在ASP.NET 2.0中使用数据源控件以及MultiView之后其底层过程还是和ASP.NET 1.x手动操作的一样,就是少写一些代码而已。

我们接着来看看带宽消耗如何,因为隐藏的控件不输出任何的HTML,因此带宽消耗就是指ViewState了。控件隐藏后,ViewState是不变的,因此隐藏控件确实比完全不加载控件造成了更多的资源消耗,换取的是该控件的状态得以保存。一般来说,简单控件隐藏后多出来几十字节的ViewState是可以忽略不计的,整个页面中HTML缩进所需的空格也都几十上百字节了;但如果是复杂控件,拥有大量的ViewState,这时候你真的应该考虑动态加载了。

总的来说,面对这类问题时首先判断显示隐藏控件的逻辑是否复杂,控件本身是否复杂。如果是比较简单的情况,则直接使用MultiView解决就是了。如果是复杂的情况,那就应该考虑自己使用控件将此逻辑封装在内,而不是直接在页面上暴露这些复杂性。关于封装控件的问题,在下一篇文章中再讨论,因此我们继续看下一类问题。

既不确定类型也不确定数量的控件

有时候我们面对前面两类问题都有清晰的思路,但是面对复合问题就感觉很混乱了。例如还是一个调查问卷的显示,数据来自XML,问题类型包括单选和多选,每一道问题的选项个数也不确定,这时候怎么办呢?foreach嵌套foreach,外层迭代问题内层迭代选项,逐个CheckBox/RadioButton来生成?

这时候我们需要的是把问题分而治之逐个击破的思想。既然是上述两类问题的嵌套,我们就应该能够通过嵌套对应的解决方案来实现。对于这个调查问卷的例子,我们可以用Repeater来迭代问题,先把这个定下来,再考虑模板里面怎么做。模板里面需要显示的是一个不确定类型的问题,因此模板里面放一个MutliView,把问题类型的表达式绑定到其ActiveViewIndex属性上,例如单选题就是0多选题就是1。然后MultiView里面的两个View各自嵌套一个Repeater,第0个Repeater迭代选项并显示为RadioButton,第1个Repeater迭代选项并显示为CheckBox。就这样就完成了,我们没写任何一行后台代码,也没有动态创建任何控件。

然后我们来分析一下这个解决方案的性能。对比起动态创建控件,它所使用的控件确实是多了一倍,因为一道问题同时创建了两组选项,一组单选一组多选,只不过其中一组被隐藏了。然而隐藏掉的那一组唯一的服务器端资源消耗就是创建以及绑定,它们不输出任何的HTML,因为它们的值不会被改变所以也不会输出任何的ViewState,并且它们也不会触发任何事件,因此在对性能没有特别要求的情况下这样的性能损失还是可以接受的。至少,这比起你自己去研究ASP.NET页面生命周期然后自己写一大段代码来实现动态加载控件要好多了。

问题与实验

本系列上一篇文章的问题与实验一直没有解答,现在给出参考答案如下:

  1. 为Page增加一个ShowCheckBox的属性:
    bool ShowCheckBox {
      get { return (ViewState["ShowCheckBox"] == null) ? false : (bool)ViewState["ShowCheckBox"]; }
      set { ViewState["ShowCheckBox"] = value; }
    }
    在OnLoad的时候检测ShowCheckBox属性,如果为true则添加上该CheckBox控件。在Button的OnClick事件中,设置ShowCheckBox为true,并添加上CheckBox。记得这两处创建的CheckBox必须拥有一致的ID属性。
  2. 这是为了让ICallbackEventHandler的处理模型符合页面生命周期的模型。虽然Callback发生的时候,页面生命周期已经与PostBack不同,然而ICallbackEventHandler还是让Callback模仿了PostBack的页面生命周期。RaiseCallbackEvent相当于PostBack的Raise PostBackEvent阶段,GetCallbackResult相当于PostBack的PreRender阶段。前者负责事件响应,后者负责生成返回客户端的HTML代码。

这次想和大家讨论的问题是,你觉得你是完美主义者吗?面对上面的调查问卷需求,你会选择我所说的Repeater套MultiView再套Repeater的做法,从而避免写任何一行后台代码,还是会选择自己封装一个控件动态创建所有控件,避免任何不必要的性能损失?

最后,如果你喜欢本系列文章,并且不希望错过下一篇关于控件开发的文章,欢迎订阅我的blog:

在下一篇文章中,我将会介绍Repeater等数据控件是如何工作的,为什么它们能够轻松应对动态创建控件的各种情况,我们如何学习这些控件的设计模式并运用到我们的开发当中。

2007年9月6日星期四

Is this cheating?

终于把《The Game》看完了。不知道是不是因为它的写作风格比较pop culture,好像Hollywood大片的台词风格,所以读起来感觉非常好,也让人非常addicted,这点与读英文的技术书不同。这本书对我来说属于少数,我的意思是能够随之而来影响我现实生活的书,那当然是少数了。

首先当然是通过这本书发现了一条self improve的途径,因为the game本身不仅仅是围绕pick up这一个game,而是关于你的self-confidence。有些人天生就很有自信,能够随便地对别人说出自己想要的,或者仅仅是想法,而不怕遭受别人拒绝;另外还有一些人则相反,他们会害怕遭受拒绝,拒绝后也就接受了,而且心里还会不舒服上一会儿,以后再次提出的胆量也就少了。当然,人不是两极分化的,所以大部分的人分布在这两种极端之间,但总的来说还是有不少人对遭受拒绝感到恐惧。

读完这本书之后,就明白了PUA都想获得的一种本能反应——“我才不在乎呢”,反正被拒绝又如何,我又没有损失的。其实很多很理性的人都能够理解到没有损失这一点,但就是心理上不愿意接受被拒绝的可能性。对我而言,要去掉这种恐惧并不难,看完这本书之后我能够很快切换到那种心态,这正的麻烦在于——要降低被拒绝的概率,心态不是主要问题,技术才是主要问题。然而要练习技术就不再是那么容易,你需要花大量时间投入到实践当中,不断摸索,然而暂时对我来说我不觉得我有必要投入那么多的时间到与不同的人交流当中去。我还有书要翻译,我还有TOEFL要准备。

这本书引发的后继事件,包括我推荐了JimRobert来看这本书,然后我又在豆瓣上发现了ePIK也在看这本书。其中Robert和ePIK都和我一样是沉迷型的——这本书正是我想要的,我缺乏的东西里面有说,我渴望这种改变。而且我们这三个人还有一个共同点——technically a virgin。我想这正是我们渴望去改变的原因,因为我们都看到了前面有路可走,我们需要看清楚这条路,以及它能够怎么走下去。然而对于我身边的大多数朋友来说,他们对此都毫无兴趣,他们不是没有这个需求,而是忽视这个需求的存在,认为这不是能够通过一本书所展开的一组技能就能解决的。我们之所以相信这本书是有效的,是因为读过之后很快就开始分析身边亲眼目睹的一些这方面的成功人士的做法,并且发现这些天生拥有此项技能的人正好符合书中的描述。

有一件巧合的事见,我们在读这本书的时候,VH1的真人秀The Pick Up Artist开播了。书中有一些事情的描述,单纯看文本是怎么都看不明白的,但如果看到真人实践那就好理解很多了。另外网上有关的资料也不少,我们都搜集回来看看。有很多的技巧,其实不仅仅适用于pick up,还适用于日常沟通,能够让对方更乐于听你说,被你所吸引。

好了,有人看到这里肯定要说我文不对题了,起了一个完全无关的标题来吸引眼球。其实不是的,题目正是我接下来要讨论的内容。首先我要说明一点,现在我是支持life is all about experience的,你必须有所体验了才能更好地作出选择。就好像我希望能到不同的国家旅游一下,这样我才能更好地决定我到底想去哪里发展,随大流挤去美国并不一定是最好的。女人也一样,你要有充分的经验你才能够更好的选择,一开始就盲目相信one-itis是不行的,必须有重复的依据说明那真的是你的one-itis才值得你去追,然而依据来源于经验,而不是无端端的感觉。

好了,这时候问题就来了,就好像我只能告诉Singzy,"you are part of my experience, but you might not be the one"。这时候就违反了常人对relationship的理解。一般的理解是,因为我喜欢你,所以我喜欢和你在一起。然而我的逻辑是,我不确认我对你的态度,然而我需要和你在一起,因为这样才有助于我确定对你的态度。这就是题目所要问的,"is this cheating?"。

当然,其实我不认识这是cheating,这对我来说不是一个问题。因为我首先要有这样的看法了,我才能继续我的experience,否则认知失调带来的麻烦将更大,至少比选择女人的麻烦要大。The Game里面提到一件事情,就是假如你认为隐瞒着一个情人去发展或维持另外一个或者一些情人的关系是不道德的,那么你就不要这样做了,因为你自己都认为是错误的事情,无论如何都是错的。但是如果你好像穆斯林那样,认为这是对的,直接告诉对方我是一个穆斯林,在穆斯林的信仰当中一个男人要有三四个老婆才好,你是爱我的话就必须接受我的一切,包括我的信仰,这样才可能有出路。因此,我也就选择了把我所知道的以及我的逻辑告诉了Singzy。

2007年8月21日星期二

ASP.NET 3.5 的 ListView 控件与 CSS Friendly

之前在写CSS有关文章的时候,我就想写写如何使用ASP.NET控件能够更加CSS Friendly,更容易实现一些常见的页面布局pattern,然而之后就发现这并非那么容易的。说起来要让ASP.NET控简变得CSS Friendly很容易,直接使用ASP.NET 2.0 CSS Friendly Control Adapters就是了,然而事实并非如此简单。

CSS Friendly Control Adapters的不足

首先请允许我对这个CSS Friendly Control Adapters抱怨一下。我第一眼看到它输出的class名称我就觉得很faint了,举一些例子:AspNet-Menu、AspNet-Menu-WithChildren、AspNet-Menu-Leaf。如果你习惯了客户端代码一律使用camel命名法的话,你看到这样的命名就会觉得无法适从,你是要改变原有的命名法来迁就这些控件呢,还是让多种命名法在你的CSS文件中混排呢。如果需要改变这些默认的class命名呢?不好意思,控件自身的CssClass属性已经没有任何作用,因为控件输出的HTML结构都改变了,那些CssClass也就不再对应哪个HTML元素了。因此,如果你需要改变这些class命名,唯一的办法就是直接更改ControlAdapter的源代码,而class命名是以字符串形式硬编码在源代码中的,就算你用搜索替换你还是会害怕替换多了或者替换少了从而引入了更多的麻烦。

说到源代码,这些ControlAdapter的第二个麻烦也就浮现了——网站必须携带它们的所有源代码,而不仅仅是编译好的dll,而且这些源代码的可修改性并不强。为什么说可修改性不强?如果你有想过自己写一些ControlAdapter的哈,我想你已经参考过现有的那几个ControlAdapter了,你会发现编写ControlAdapter严重依赖于你对该Control本身的理解,不仅仅是对Control公开部分的了解,还需要对Control内在逻辑的深入理解。因此,要么你是Control的作者本身,要么你就细看过Control的源代码,否则不可能写出ControlAdapter,甚至修改已有的都很难。

因此,CSS Friendly Control Adapters是一个非常之鸡肋的选择,我们不如向前看,看看Microsoft在ASP.NET 3.5中为我们提供了什么。

ListView以及全新的TemplateControl形式

ListView是ASP.NET 3.5新引入的一个控件,如果你还没有使用上Orcas,或者没试用过这个控件,那么不妨看看ScottGu的介绍性文章:The asp:ListView control。这篇文章详细说明了如何先设计一个原型页,然后设计LINQ to SQL以便获取数据,在将数据绑定到ListView上面,最后还加上DataPager分页。我们不需要看那么多,看ListView那部分就是了,看看声明ListView的代码。

如果你熟悉之前Atlas提供的Sys.UI.Data.ListView,那么你一定会觉得这两个ListView很相似。与之前的TemplateControl(例如GridView)不同,ListView不再直接输出容器本身的代码,而提供了一个Template给你自定义容器,你可以在这个Template中自由编写你的容器代码,它可以是<table />,也可以是<ul />或<ol />。之后项目的Template也是允许自定义的,对应<table />的自然是<tr />,而对应<ul />与<ol />的则应该是<li />。因为这些都是你手动编写的HTML代码,所以你可以随意地给它们设置class属性,从而让你能在整个网站中保持命名风格一致性。

Web Form的屈服?

ASP进化到ASP.NET的时候,好像Win Form那样的拖放控件支持成为了最大的特色,然而现在Web Form的编写方式又变回和其它服务器端脚本语言(例如VBScript)差不多了。以前ASP的时候,不就是自己写容器的HTML咯,然后用<%For ... Next%>把项目HTML圈起来,现在改为叫做模板其实没什么差别啊,况且其他服务器端脚本语言都有类似的写法,不过可能是helper函数或者别的称呼,都差不多。

因此,事实证明除非放弃对HTML细节的控制权(而这又难以做到CSS Friendly),否则对于大多数服务器端语言来说声明数据表现模板的方式都是类似的,没有更便捷的方式了。能够省事的是数据访问方法,从ADO进化到ADO.NET,从Typed DataSet到LINQ to SQL。将来Microsoft是否会发布更多类似的TemplateControl还很难说,因为ListView已经有非常高的可定制性,原来用来表示二维表数据结构的DataControl都可以用它作为替代品,同领域的控件已经没意义了,不像以前要分开几个DataControl了。我觉得接下来最好能看到一个取代Menu的CSS Friendly Control,因为Menu所表现的数据结构不是二维表,而是树,有必要为这种数据结构提供一个能准确声明HTML细节的控件。

最后,如果你对我的blog中关于ASP.NET与CSS的文章感兴趣的话,可以考虑订阅:

2007年8月13日星期一

买了两本 Photoshop 的书

两本都是《选择的艺术》这个系列的,其中一本是《Photoshop CS图像处理深度剖析》,另一本是《Photoshop CS图层通道深度剖析》。买Photoshop的书,是因为工作中要用到,作为一位web设计人员,单纯需要编写代码的工作暂时还不存在(虽然Microsoft的产品线设计尝试让设计师与程序员能够低耦合度合作),因此学好一些基础的设计思想与设计工具运用知识还是有必要的。

例如Google的Webmaster职位,要求就包括Adobe Photoshop操作知识。我暂时只能用Fireworks做一些简单的mockup,mockup的素材没办法自己用Photoshop处理,这就大大限制创意的发挥。另外我还想去学学摄影,因为在我还不能什么都用Photoshop画出来之前,素材的获取有时候就必须依赖于实物,看来要补的知识还真是一大堆啊。

2007年8月8日星期三

为什么 Flixster 比豆瓣更好

虽然我主要适用豆瓣记录书、电影、音乐,然而用过Flixster之后就为它所吸引,希望豆瓣能够加上Flixter那样的功能。

在我认真使用豆瓣之前,Piggest跟我说过豆瓣的优点:在你注册并添加少量已经看过的电影后,你会发现你想继续添加的电影通常已经出现在“豆瓣猜你会喜欢”的列表上,这时候添加就方便很多了,因为无需再按名字搜索,直接点击就是了。然而Flixter更先进,它根据大家看过电影的历史分析出所谓的经典电影,也就是人人都看过的,没看过至少听说过的,在你注册后就提供这些经典电影给你评分。假如我没看过一部电影,而且也不想看,怎么办?Flixster比豆瓣多了一个“不感兴趣”的选项,这应该算是一个更体贴用户的设计,而这个设计豆瓣后来也加上了,不过不是一个直接的选项,而是你可以选择隐藏“豆瓣猜你会喜欢”列表上的推荐项目,那也相当于是不感兴趣吧。

对于一个Web2.0的网站来说,数据是很重要的,掌握的数据越多,就能越有效的服务现有用户,对新用户的吸引力也越大。在获取数据方面,Flixster的做法显然比豆瓣更好,对经典电影的评价几乎人人都有,而此时以推的形式主动要求用户评分并不会让用户觉得觉得不喜欢,反而提供了便捷,即使有用户不喜欢这样,他也可以选择之际跳过这个步骤。类似的,其实很多Web2.0网站都可以在用户注册的过程最后加上一个可选步骤,要求用户提供更多信息,而网站要设计好获取信息的方式,让用户觉得不用动脑筋就能填写,并且提醒用户在这里填写能够更便利更快得到全面的服务。

2007年8月1日星期三

今天开始翻译 Prototype and Scriptaculous in Action

感谢Dflying Chen的联系与推荐,让我有机会为图灵公司翻译Prototype and Scriptaculous in Action的简体中文版。这本书的作者包括Ajax in Action的作者Dave Crane,同时作为引进大陆的首本关于Prototype的书,因此估计将会热卖。

基于上述因素,翻译此书时我必须特别小心,避免任何翻译错误甚至仅仅是可能引起争议的地方,因为一旦这书上市了必定有不少读过英文版的人来读中文版,接着就可能指出他们认为翻译得不够好的地方,即使那个地方是纯粹文学性的并且与书中讨论的技术细节没任何联系。现在是Web2.0时代,你犯的任何错误都将被别人永久的记录在Internet上,如果第一本翻译的书出问题了那么将来就很难再被信任以担任同类工作,因为读者认为你的翻译不好的话出版社就敢让你翻译。实际上不仅仅翻译工作如此,现在的Internet逼着你我以及任何一个人做事时都必须加倍小心,面对公众所犯下的错误都将被一大堆人记录到他们的blog上,这种负面影响难以用你的个人之力以消除。当然,事情也有好的一面的,如果翻译做好了将能得到相当的赞誉,这种来自公众的赞誉也不是别人轻易能够克隆的,因此也有相当的含金量。

最后,如果你希望对我的翻译工作提供任何的建议或支持,或者你有兴趣和我合作翻译,欢迎留言或联系我。直接在blog回复留下联系方式的话,我将会主动联系你。