2006年10月31日星期二

深入理解 ASP.NET 动态控件 (Part 1 - 感性认识)

正如我在《我喜欢的教材与我讨厌的教材》中所说的,我讨厌那种标题之后直入理论部分并开始写“定理1、定理2、定理3”的做法,所以在我自己的文章也绝对不会这样写。我认为感性认识是理性认识不可缺乏的基础条件,所以在很理论性的解释ASP.NET页面生命周期之前,先通过一些大家可能都遇到过的例子给大家一个感性认识。

动态控件遇到的第一类问题就是跨页面生命周期时无法自动保存,你必须每次手动创建。举个简单的例子,例如现在我有一个DropDownList,有三个ListItem,值分别是"0", "1", "2",在我设置了AutoPostBack之后,我希望SelectedIndexChanged时根据我选择的ListItem数值动态创建相应数量的TextBox,简单的代码如下:
protected void dropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
for (int i = 0; i < dropDownList.SelectedIndex; i++)
{
TextBox dynamicTextBox = new TextBox();
this.Form.Controls.Add(dynamicTextBox);
}
}
需要解释一下的是,直接用dropDownList.SelectedIndex是为了省事,因为ListItem的值本身也就是从0开始的顺序整数。

测试一下我们这个小小的ASP.NET程序有没有问题,结果当然是没问题的,你选择了哪个数值就真的会有相应数量的TextBox出现,好简单哦!我们再扔一个Button到页面上看看又会怎样,这时候你就会发现如果通过点击Button导致PostBack,那么动态创建的TextBox就没掉了,看起来事情并不如我们期望的那么简单。

“我们已经知道这个问题啦,快点给出解决方案啦”——如果你急需要一个解决方案,请直接看本篇文章的最后几段。我知道很多人是因为当前有一个棘手的问题才来翻看这类文章的,但我也不能因此而忽视了另外一部分人的需求——他们希望由浅入深地了解这个问题,并且得到解决方案的同时得到完整解释。

接下来我们继续来看第二类问题,动态创建控件的事件触发不正常。我们又来写一段简单代码:
protected void Page_Load(object sender, EventArgs e)
{
TextBox dynamicTextBox = new TestingTextBox();
dynamicTextBox.ID = "DynamicTextBox"
dynamicTextBox.Text = "InitData"
dynamicTextBox.TextChanged += new EventHandler(dynamicTextBox_TextChanged);
this.Form.Controls.Add(dynamicTextBox);
}
void dynamicTextBox_TextChanged(object sender, EventArgs e)
{
this.Trace.Write("DynamicTextBox", "TextChanged");
}
由于用到了Trace,测试的时候别忘记把Trace打开哦。

我们再扔一个LinkButton到页面上,目的仅仅是为了触发PostBack,然后看看事件是否正常。奇怪的事情发生了,在修改TextBox的值之前,无论怎么点那个LinkButton,一切都非常正常,TextChanged事件确实不发生。修改了TextBox的值之后点LinkButton,事情也还正常,TextChanged事件发生了。但之后就出问题了,无论你是否修改了TextBox的值,TextChanged总是在每一次PostBack时都被触发。

这个问题很怪异对吗?事件既非完全不触发,也非总是触发。其实答案隐藏在我之前那篇《深入理解 ViewState》里面,去读一读那篇文章,或许你自己也能够解释为什么会这样。

动态创建的控件或许还存在第三类、第四类问题,在此就不一一列举了。我相信被动态控件问题困扰过的ASP.NET程序员绝对不少,而未遇到过此类问题的程序员看到上述两个问题也未必能给出解决方案和正确解释。

在提供问题的解决方案之前首先要说明一点,作为ASP.NET程序员的你需要在某一时刻某一地方让控件动态出现时,就立即在该处写代码动态创建并添加控件,这往往都是错误的做法。正确的做法是向后退三步再抬头看,这时候你看到的就不是你要让控件动态出现的那一个准确的时刻和地方,你应该看到ASP.NET页面生命周期的全貌,接着你就应该清楚你的代码该加去哪里了。

好了,是时候给出最直接的解决方案了,唯一的解决方案就是让你看清楚ASP.NET页面生命周期的全貌,而其中最佳的入门方式就是学习控件设计。虽然上面把动态控件说成一个复杂的问题,然而大家天天都在用动态控件,只不过动态控件已经被封装到一个静态控件里了。例如复杂的GridView控件,它会自动根据每一列的性质来生成对应控件,如果是模板列还要分析模板中的内容来生成模板中定义的控件,这些控件都算是动态控件,为什么PostBack不会让他们自动消失,为什么为它们添加的事件从来不会错误触发,在你学习完控件设计之后就会一清二楚。

关于控件设计,我推荐大家买Wrox(乐思)的书来看,是以控件设计为主题的那两本,不会很厚,很快能看完。如果你在使用的是ASP.NET 1.x,或者你一定要看中文版的书,那么ASP.NET服务器控件高级编程将是一本很适合你的书。至于ASP.NET 2.0的则有Professional ASP.NET 2.0 Server Control and Component Development,英文版今年8月才发布,根据清华出版社的惯例至少要等半年才可能有对应中文版。

既然连解决方案都给出了,这个系列的文章继续写下去还有什么意义吗?书上能给你的只是一个临摹着去做就不会出错的模式,以及一个听起来很合理的解释。到底为什么临摹这种模式去做就符合ASP.NET的大模式(主要是编译模型和页面生命周期),ASP.NET的大模式到底是怎样的,这就是我接下来要写的东西。

2006年10月30日星期一

扩展 Atlas 的客户端 Web Service 调用功能

Jeffrey Zhao最近在写客户端调用WebService的有关内容,那么我也来说说。Jeffrey Zhao说到了,Beta1中只有一个onComplete,onTimeout、onError、onAborted都没有了,这3个事件都整合到onComplete中,使用者需要自己在onComplete中手动判断到底属于哪个情况。另外Jeffery Zhao最近在讨论继承WebRequestExecutor中说到,XMLHttpExecute._onReadyStateChanged无法重写,所以要跟踪返回就要在WebRequestManager.completeRequest事件中进行。

这样看来,Beta1的WebService基础代码确实不太友好,我虽然还没有在Beta1用过WebService,但看样子有必要改善它。这次又到讨论继承哪个类?这个不一定要用继承嘛,除了我们熟悉的继承,以及JavaScript天生就支持的prototype模式,我们还可以考虑decorator模式。

由于我还没开始弄Beta1的WebService扩展,所以在这里仅能提供一段以前写下的用于Atlas June CTP的Web Service调用封装,调用方式为:
Cattism.BodyMaintainer.Web.Services.call(method, arg1, arg2, ..., configuration);
其中,method是带调用WebService的代理方法,之后arg1, arg2, ..., configuration就和直接调用WebService代理方法时使用的参数列表一致。该封装提供4大回调函数的一致性处理,例如如果你需要onError时一律将Exception信息通过debug.trace输出,那么你可以在Cattism.BodyMaintainer.Web.Services._onError中添加相应代码。你甚至还可以要求仅当调用时没指定onError回调时才将Exception信息通过debug.trace输出。

有人会想说我提供的代码看起来不太像decorator模式,对吗?其实我写的时候是完全根据自己的需求写的,写完才想起来这应该做成一个标准的decorator模式,不过也懒得改它了。在这里我只是希望给大家提供一个思路,就是当你觉得Atlas还不够好的时候你应该主动去添加一些代码让它能够满足你的需求,同时思维不一定要限制在基于Atlas的修修补补,你完全可以对Atlas进行二次封装制作一套适合你的个人工具库或者企业类库。

2006年10月29日星期日

深入理解 ViewState

上个星期写了一篇《控件 ViewState 属性的值保存去哪里了》,解释了Control.ViewState最终还是通过Control.SaveViewState和Control.LoadViewState这两个方法存取的。文章中有一句话可能会让大家感到疑惑的:“我们在OnInit之后使用this.ViewState[key]读写时该属性都为true”,其中“该属性”指StateItem.IsDirty。到底为什么IsDirty属性在OnInit之后总是为true呢?参考了TRULY Understanding ViewState,我终于明白到其实它并非总是为true,详细原因请听我慢慢说。

首先要让大家来看的是StateBag.TrackViewState方法,这个方法在控件OnInit时就会被调用,而它的作用就是让StateBag开始跟踪StateItem的变化,任何变化都将导致该StateItem的IsDirty属性变为true。也就是说,在OnInit之前,IsDirty属性是false的,并且无论你如何设置Value属性的值都不会改变IsDirty属性。在OnInit之后,IsDirty属性也保持着false,直到你第一次改变Value属性的值(指通过this.ViewState[key]的方法改变)。到了SaveViewState的阶段,只有IsDirty属性为true的StateItem才会被保存下来。

为什么要如此设计呢?例如一个通过声明性定义的Label的Text属性,在ASPX中它被赋了初值,然后该初值自然通过ViewState["Text"]来持久。在下一个页面生命周期,首先OnInit时这个Label的Text属性会加载ASPX中声明性定义的初值,然后LoadViewState时会用ViewState中读取到的ViewState["Text"]来覆盖它。然而除非你在上一个页面生命周期以编程的方式改变了Text属性,否则ViewState["Text"]还是初值,那么你就是用ViewState["Text"]保存初值去覆盖声明性定义的初值,同一个值这样覆盖完全没意义,而且还浪费了ViewState的空间。为了解决这个资源浪费的问题,凡是声明性定义之后没改变到的值就不应该使用ViewState来持久,而详细的实现就是上面说的TrackViewState机制了。

说到这里,Control.ViewState已经解释完毕,如果你是控件设计者你可以放心地按以下方式把控件属性存放到ViewState中:
public string Text
{
get {return this.ViewState["Text"] as string;}
set {this.ViewState["Text"] = value;}
}
它的内部机制会懂得区分你存进去的值是不是ASPX上声明性定义的初值,然后决定是否持久该值。同时,如果你在任何阶段想改变一个ViewState值是否持久的决定,可以通过ViewState.SetItemDirty(key, dirty)来改变,这基本上已经满足了所有控件开发人员的需求。

2006年10月27日星期五

Microsoft Ajax Beta1 - 边学边用边补充 (Part 2 - DragDropList)

由于我在做一个类似Live.com的东西,所以需要类似WebPart的功能。我不清楚Atlas扩展的那个所谓的Cross Browser WebPart到底是什么,支持如何,所以不敢去尝试,从而决定用DragDropList。

首先,关于如何使用DragDropList,可以参考Dflying的以下两篇文章:

参照第二篇文章轻松制作出WebPark拖放效果来是完全没问题,接下来要做的就是让用户拖放修改后的界面保存下来。这时候ASP.NET 2.0新增的Profile Service就是一个很好的选择,而且Atlas也包括对Profile读写的支持。接着问题就来了,何时能够获取到拖放结束的事件然后更新Profile呢?DragDropList和DraggableListItem没有对外公开任何事件,所以在使用它们快速实现类似WebPart的功能时仅仅能够获得视觉上的效果,无法对它们编程以实现任何有实际意义的东西。

这时候我选择了继承DragDropList来增加对外的事件触发能力,需要做的仅仅是重写onDragEnd方法。感谢Beta1的prototype书写方式,所有函数都是virtual的,这给重写带来了方便,不需要再用registerBaseMethod覆盖掉基类的函数。重写如下:
function MyDragDropList$onDragEnd(cancelled)
{
  debug.trace(cancelled);
  MyDragDropList.callBaseMethod(this, "onDragEnd", [cancelled]);
  this._dragVisual.style.width = null;
  this._dragVisual.style.height = null;
  this._dragVisual.style.zIndex = null;
}

debug.trace(cancelled)在这里是做演示作用,真正放在此位置的应该是一条触发事件的指令,甚至可以根据cancelled值决定是否触发,因为若它为true则实际上拖放没造成任何变化。

后面对样式属性的删除又是什么呢?这是对DragDropList某一个bug的work around。在拖动过程中,由于拖动的DomElement(也就是this._dragVisual)脱离了它原本的上下文,如果它原本的样式属性是width:100%那就会出问题,所以DragDropList取了它的offsetWidth属性来作为它的width,这样width的单位就一定是px。然而这却造成了另外一个问题,拖动后DomElement的width变成了一个单位为px的值,用户缩放浏览器时它就不能跟随缩放,这不符合我在CSS文件中定义width的单位为%时所期望的。于是我就在拖放结束后删除DragDropList对此DomElement样式的影响,让CSS文件定义的样式重新成为当前样式。

最后,提供一篇我觉得比较有价值的文章,方便大家实现自己的IDragSource和IDropTarget:Drag and Drop with Atlas: interfaces。文章中详细解释了这两个接口的各个方法,比起你自己去研究DragDropList的代码要简单一些。

Microsoft Ajax Beta1 - 边学边用边补充 (Part 1 - Debug)

这Beta1的更新多就不再强调了,关键就是它对以客户端为中心的开发人员不太友善。客户端框架正在作大幅度修改,这个我不反对,但是将明显一个修改到一半的东西扔出来显然就不太好。Beta1里面的客户端框架是没问题的,不过Preview中的就衔接不上了,有些CTP原有的功能没有在Preview中保留,有些则看得出是修改中的,例如debug版的代码不太工整,enclosure和prototype书写方式混合。

将老项目由July CTP迁移到Beta1 + Preview,第一件事当然是测试更新后原有功能是否正常,不正常的地方是由什么引起的,但我马上就发现可爱的debug.dump和debug.trace都不见了,这样我可没办法做项目迁移啊,因为连基本的debug功能都没有。搜索了一下Beta1的代码,发现只有debug.assert和debug.fail;而在Preview中则有如下一段代码:
if(!debug.trace) {
  debug.trace = function debug$trace(message) {}
}
这段代码的用意很显然,debug.trace已经被去掉了,然而CTP迁移而来的代码中可能有用到debug.trace的,这样做能避免对debug.trace的调用引发脚本错误。

为了完成迁移,首先要把debug.dump和debug.trace补上,尝试的就是把July CTP的代码借过来用。代码借过来后,debug.trace基本没问题,就是第一行那个Debug.writeln不知道用来干什么的;debug.dump的问题就一大堆,这是由于一些CTP中存在的类改名了或消失了,例如Sys.IArray、Sys.ITypeDescriptorProvider、Type.Event。我开头的做法是把debug.dump中不能用的代码段注释掉,后来Jeffrey Zhao为ASP.NET AJAX 1.0 Beta补充trace和dump功能一文中提供了一个更好的版本,我也就参考着那个作了修改,制作出我自己的版本:用于MS AJAX Beta1 + Preview的debug补充包

由于我还没有仔细看过Beta1的原代码,所以采用了保守修改的原则,也就是尽可能少的改动。因此,我仅仅作了如下替换:

  • Sys.IArray.isImplementedBy -> Array.isInstanceOfType
  • Sys.ITypeDescriptorProvider -> Sys.Preview.ITypeDescriptorProvider
  • Type.Event-> Sys.UI.DomElement

 

需要说明的是,Jeffrey Zhao那个适用于不加载PreviewScript.js的场合,而我的这个必须和PreviewScript.js一起使用并且在其之后加载。另外为了方便我直接在浏览器地址栏调用debug.dump和debug.trace,我为它们提供了更简短的别名:$dump和$trace。

2006年10月23日星期一

博客重构 / Blog Refactoring

之前曾经我的Blogger上说过将技术贴存档到一个专门的地方,同时开一个英语blog。现在第1步已经完成了,我在cnblogs开了一个新的blog,名称为Cat in dotNet,所有纯技术贴都已经迁移到那边去了。以后我的技术贴,在Blogger发的时候同样会在cnblogs发,不过对于技术贴的定义我需要做一个详细的说明:

  • 主要是以讨论.NET Framework、C#、ASP.NET、Atlas相关技术为中心的帖子。
  • 以揭示运行机制及undocumented功能为主,同时也有FAQ类型的帖子。
  • 纯猜想性的帖子不包括在内。

 

cnblogs的好处是能够贴代码,这是Blogger所不能的。不过既然我已经习惯了只贴小段代码,并且自己格式化,那以后也坚持这样做,因为贴大段代码要读者自己找出其中关键部分也不是一个好的做法。

至于英文blog,我准在各大英文BSP中挑一个然后开始好好写,目标是能够和英文社区的native speaker混在一起,哈哈。

2006年10月21日星期六

ASP.NET AJAX Beta1 发布

Atlas在7月的CTP之后就几个月没有更新了,当然就意味着之后会来一个大更新,这就是ASP.NET AJAX Beta1。ASP.NET AJAX Beta1将原来的CTP拆分为两个部分:1.0核心增值CTP,同时原来的Atlas Control Toolkit也改名为ASP.NET AJAX Control Toolkit了。上述3个下载都已经更新了,然而要把老的Atlas项目迁移到新版本则不那么容易,需要看着迁移向导来做一系列的修改。

作为一个大更新,更新项目当然不少,这个可以参考Changes between the ASP.NET AJAX (“Atlas”) CTP and the v1.0 Beta/RTM Release。这份参考是在巨大的可怕(打印版本为49页),红字绿字标出了大量RTM和CTP的不同之处,我猜这足以让老的Atlas程序员放弃更新他们的老项目。当然,也不要为了这些巨大的改动而停滞不前,因为这些改动中的大部分都是响应来自社区的要求,将Atlas的代码结构尽量简单化,让更多人能够更容易的理解Atlas。如果你想看一份能让你开心看完的更改列表,不妨来看看Scott GuthrieASP.NET AJAX Beta 1 Released,这些新特性也足以鼓励你在新项目中采用新的版本及使用新的代码书写方法。

2006年10月18日星期三

.NET 里 String 的特性

String是指System.String,同时string关键字也是System.String的别名。这里要说的特性都是一些让不理解的人容易犯错的特性。

String的第一个特性就是它是引用类型,但很多时候表现起来却像值类型,这是第一个让人容易犯错的地方。String有一个不可破坏的特性(immutable),或者简单叫做只读特性,这意味任何改变String的操作其实都没有改变原本那个String,而是创建了一个新的String实例同时让变量的引用(指针)指向了新String。这个特性让String在某些方面表现得像值类型,例如:

  • 作为函数的传入参数时表现得像值类型,也就是如果你传入了一个String,然后在函数内改变了它的值,并不会对函数外面该值原本的引用有任何的影响。
  • 把一个String赋值给另一个String,改变其中任何一个String另外一个都不会受影响。

然而这不影响String作为一个引用类型的本质,例如它可以是null,也可以用于lock。

String的第二个特性就是字符串池(String Pool),也叫做拘留池。程序运行时所有的String其实都存放在一个池中,任何一个特定内容的String在池中仅会有一个副本,所以多个内容相同的String对象其实都是引用同一个字符串副本。这特性意味着你操作两个String如果内容可能相同你就要格外小心了,例如:

  • 在执行lock的时候,如果放进去的是一个String,那么当两个线程中该String内容一致时其中一个就会阻塞,因为这两个String其实引用同一个字符串副本。

 

在了解到这些String特性后,编写程序时遇到String的传递和比较就要格外小心咯,想清楚String的行为会是怎么样的,再决定如何写代码。

控件 ViewState 属性的值保存去哪里了

看过MSDN的都知道,存取ViewState有两种方法:

  • 直接操作控件的ViewState属性,通过this.ViewState[key]就可以直接进行读写。
  • 重写控件的LoadViewState和SaveViewState方法。在LoadViewState中系统会将此控件以ViewState保存的信息作为一个object类型参数传入,控件需要自己将信息unboxing出来。在SaveViewState中,控件需要自己将想通过ViewState保存的信息boxing到一个object里面,然后return给系统。

那么到底这两种方法读写ViewState有什么不同呢?

使用Reflector看看Control的LoadViewState与SaveViewState方法你就会发现,其实控件的ViewState属性也就是一个特殊的控件属性,类型为StateBag,由于Control已经为你写好了将StateBag存取到真正的ViewState的方法,所以只要你继承Control控件你就可以放心地把值存到StateBag里面去,而这些值最终会保存到真正的ViewState中。

就这么简单?还差一点,就是StateBag这个字典的每一个项目类型为StateItem,而StateItem有一个IsDirty的属性。只有这个属性为true的StateItem才会被保存到ViewState中。我们在OnInit之后使用this.ViewState[key]读写时该属性都为true,所以StateItem都会被保存。但如果你想要某个StateItem临时不保存到ViewState,那就可以执行this.ViewState.SetItemDirty(key, false)。例如我们熟悉的TextBox,在它的TextBoxMode为Password时它就会通过上述方式让this.ViewState["Text"]值不保存到真正的ViewState中,也就确保了你无法通过ViewState窃取密码,同时也导致了该TextBox的OnTextChanged事件无法正常触发。

那还有什么要说的吗?要说明的就是StateBag的存取所受到的限制。StateBag的存取,与你手动重写LoadViewState/SaveViewState保存的其它值一样,在LoadViewState之前StateBag并没有任何值,在SaveViewState后的任何更新不保存到ViewState中。

2006年10月15日星期日

别把 identity 都放一个 website 里

俗话说别把鸡蛋都放一个篮子里,identity也不能都放一个website里。

现在很多web 2.0网站都希望用户在上面慢慢建立自己的identity,通过这方面粘住用户,这确实是个好办法,然而对于用户来说是有风险的。将自己的identity都放在一个网站,将自己的创意与作品都放上去,如果这个网站倒掉了,或者改变其策略变得对你不利或者你不喜欢了,这时候你就麻烦了。因此,将自己的identity分布于不同的网站是有必要的,这是一种基本的安全策略,关键就是如何分布才能为自己和读者带来便利。

我现在正准备重新设计我的identity,首先要解决的就是blog文章的归属问题。现在我主要利用这个blog发表技术文章和思考,也用mywallop发表一些无关紧要的东西。未来我打算将纯技术文章分离出来,或者专门找一个地方进行存档,这是因为我的纯技术文章中有部分是起着FAQ作用的,方便浏览者查阅很重要。接着我要把我的wallop.com也启用了,SNS的好处是邻近的人能够很容易就留意到你的更新,而不邻近的人可能永远都不会注意到你在写什么,所以我会写一些和我的social network有关的东西。最后就是我计划开一个英文的技术blog,是时候练习一下用英文写些实实在在的东西里,这样也有利于我在英文技术社区里建立identity。

2006年10月10日星期二

看看英文 blog 写写中文 summary

在之前的《中国IT从业人员学习障碍:心态大于语言》中说到,接触前沿技术的人都是在英文技术社区里泡着的,而中文技术社区的保守心态会让人不愿意将英文资料翻译为中文。最近又碰到一些人问我哪里有比较多某某技术的中文资料,回想起我以前也时常对某技术感兴趣却因找不到中文资料而直接放弃,所以直接回复求中文资料的人:这样的东西在国内算是“前沿”,还是老老实实学好英语在Google上搜索英文资料吧,想在中文领域淘金最后通常只落得两手空空。如果你在此领域有一定了解后,多多翻译资料造福中文社区,后来者才有金可淘。

现在我也慢慢习惯了去英文社区泡了,虽然多数是潜水,看一些PageRank高的技术blog也不错。这是因为英文社区才能找到一些比较geek的用户——喜欢弄懂一样东西的运作机理、喜欢探索undocumented功能的使用方法,而这些也正是我喜欢的东西。在中文社区,是很难找得到人和你沟通与分享这方面的知识的,最多就是为一些实际问题互相帮下忙,你提一个探索未知领域的问题往往得不到任何回复。

接下来我准备做的事情,就是在看完英文资料后用中文写写摘要,算是对中文社区的一点点贡献吧。翻译我是不会做的了,首先懒得去跟原作者沟通,其次我通常不会直接buy某一篇文章的idea除非我自己亲自做一些小research来证明,所以写出来的东西也就肯定和我参考的文章有很大的不同,在文章中对参考添加链接也就算了。

2006年10月9日星期一

AJAX - 服务器端也用 JavaScript 不好吗?

现在ASP.NET要实现AJAX已经不难了,可以用同时有服务器端扩展和客户端框架的Atlas,也可以选一个Atlas之外的服务器端JSON串并转换器和支持JSON-RPC的客户端框架。类似的,PHP等现在热门的语言都有服务器端JSON串并转换器,唯独已被人放弃的ASP没有,所以很多人都为如何在ASP上实现AJAX而感到困惑。

要在ASP实现AJAX,首先考虑的是用什么数据格式进行传输,主流的选择就是XML和JSON:

  • XML - 服务器端没有ASP.NET Web Service,只能采用Microsoft SOAP Toolkit来提供Web Service,这个Toolkit不能够直接将ASP代码暴露为Web Service,只能将VB6编译的dll暴露为Web Service。
  • JSON - 服务器端缺少转换器。VBScript没有JSON转换器,同时由于这是过时的语言,也就不可能有人为它开发JSON转换器。

说到这里,细心的人肯定会指出ASP不仅仅可以用VBScript写,用JScript写也可以。服务器端使用JScript其实就是使用JSON的突破口,因为JScript类似于JavaScript,JavaScript的JSON转换器在JScript中可以直接使用,根本不需要另外编写。

其实现在已经有用JScript编写的ASP实现了AJAX,使用ASP+JSON搜索一下就能得到一堆结果。例如Simple AJAX (with JSON) Chat Application for ASP 3.0,这是一个用ASP实现的聊天程序,语言选择是JavaScript/JScript,数据传输采用JSON。

说到这里,就要回顾一下我之前那篇《Script# - 把 C# 编译为 JavaScript》。将C#编译为JavaScript是一个好主意,而且Script#的实现也很出色,让C#成为核心语言也无可厚非因为它确实是很好的语言。不过JavaScript也是也门很好的语言哦,为什么不能让它成为核心语言呢?

现存的JavaScript引擎不少,在标准的实现上略有些不同,而各自的bug当然也有多不同,但是这不应该成为JavaScript成为服务器端语言的障碍。使用JavaScript作为服务器端语言的好处是,现在已有的客户端框架都能直接拿过来用,XML和JSON都可以使用了。JavaScript最缺的应该是一些执行服务器端专有任务的框架,例如数据库和文件读写,同时也缺乏一些低层操作的能力例如Stream和Thread。然而不能够因此而歧视一门脚本语言,RoR不就是一个成功的例子吗?如果有一个JavaScript引擎能够用于服务器端任务,相信技术社区就能出产不少有用的框架来让其功能变得强大,或许能够比RoR更快热门起来——因为现在懂JavaScript的程序员肯定比懂Ruby的多。

2006年10月7日星期六

XNA - Microsoft 平台的新游戏框架

最近突然对DirectX又有点兴趣(之前有过n次兴趣,都是轻轻碰一下让DirectDraw绘下图就不想弄了),于是就下载了最新的DirectX SDK下来,想看看现在的Managed DirectX(MDX)有什么好入手的地方。结果发现最新的DX SDK也只有MDX 1.1,那么MDX 2.0在哪呢?上网以搜索才发现,原来MDX 2.0的开发计划已经终止而技术支持也将中止,MS推出了XNA这个新项目,是类似MDX是托管的游戏开发框架,并且部分MDX 2.0支持的功能都迁移到XNA中了,看来XNA就是MDX 2.0的后继版本了。

其实之前就知道有XNA这东东,但是没深入了解,和大多数人一样觉得这是一个肤浅的游戏开发引擎,想着用XNA也就是拖动放入简单的游戏元素然后用C#写几句脚本,所以没花时间去了解。现在发觉它是MDX 2.0的后继版本,想着MDX并不是一个肤浅的东西,所以深入了解也好吧,于是搜索了一些专注于XNA有关技术的站点来看看,发现它的创意确实不错,而且目标也很好,只不过很多人误解罢了。

我们来看看XNA官方blog的第一张帖子XNA Then and Now Part One,里面解释了为什么MS要提出XNA这个项目,以及项目的终极目标是什么。XNA开发队伍调查了现在的游戏开发者开发过程中遇到的问题,然后列了一些:

  • 游戏开发团队中的大部分都是美工和编剧,但真正有话事权的却是那些宁愿专注于引擎开发的程序员。
  • 由于惧怕财务风险而毫无创新。
  • 由于长期开发团队精疲力尽兴趣下降(称之为burnout),同时没有足够的毕业生填补空缺。
  • 制作游戏如此困难——指编写代码的技术性感觉方面。美工素材如此复杂和混乱让你的C++指针在此毫无发挥的余地。
  • 设计一个游戏并不容易。API、开发工具等如此多代码都是大师级程序员在脑袋里写成的。然而很不幸,最好的游戏设计师通常不是最好的程序员。

站在MS的角度来说,如此多的问题实在太不幸了,游戏开发商都保保守守开发一些成功游戏的仿制品,因为这样做财务风险低,收入多少比较容易预测。这样下去MS的XBox360肯定没办法卖,因为没有真正有好创意的游戏。为了解决此困境,MS推出了XNA,期望能够让创意重新变成游戏开发商的关注点,降低创意带来的风险,让MS的平台(Windows和XBox360)上有创意的游戏更多一些。