代码改变世界

从Atlas到Microsoft ASP.NET AJAX(3) - Class and Type Definition, Reflection APIs

2006-10-21 15:00 Jeffrey Zhao 阅读(...) 评论(...) 编辑 收藏
Class and Type Definition, Reflection APIs

在CTP版本中,您能创建各种各样的类型,例如类,接口,枚举和标记(flag)。这些功能在RTM版本中被改变了。

Abstract and Sealed Classes

在CTP版本中,您能够创建抽象类(abstract class)和密封类(sealed class)。在RTM版本中由于使用上原因,并且希望改进性能,我们去除了抽象类和密封类的概念,这使我们能够大大减少类库中的脚本数量、复杂性以及实现这些功能所代来的性能影响。

您在RTM版本中依旧能够使用register*形式的API定义命名空间、类和接口,但是它们从CTP版本中的Function.prototype转移到了RTM版本中的Type.prototype上。这对您的时候不会造成什么影响,因为在RTM版本中,我们将window.Type属性作为了Function的别名。尽管Function.*和Type.*都能够被正常使用,我们还是为此改变了推荐的使用方法。

我们在Type.prototype中定义了各种有关类型的API和基于“反射”的API,以提供一个牢固并逻辑性很强的设计模式。


Creating Types: Single and Multiple Inheritance

和CTP版本一样,RTM版本在开发时,能够继承一个类并且实现多个接口。在CTP版本中,您能定义一个有多个父类的类型,然而在RTM版本中,您只能使用单继承。

Interfaces

接口的定义方式在RTM版本中被改变了,新的定义方式使用了prototype模型,并且移除了对于抽象类的使用。下面的例子展示了在CTP版本中接口的定义方式:
Custom.ICustomContract = function() {
    
this.get_member = Function.abstractMethod;
    
this.getAnotherMember = Function.abstractMethod;
}
Custom.ICustomContract.registerInterface(
"Custom.ICustomContract");

在RTM版本中接口的定义方式和上面很相似,但是使用了在构造函数中抛出“强类型”异常的方式来避免实例化一个接口对象。在这里,我们并不会定义抽象方法,也不会定义具体实现。在Release状态下,接口不会有任何的代码或者成员定义。
Custom.ICustomContract = function() {
    
throw Error.notImplemented();
}
Custom.ICustomContract.registerInterface(
"Custom.ICustomContract");

Custom.ICustomContract.prototype 
= {
    get_member: 
function() { throw Error.notImplemented();},
    getAnotherMember: 
function() { throw Error.notImplemented(); } 
}

当您在定义一个类型时使用接口,能够使用类似“反射”的API来测试这些类型。当您在实现接口时,您为自己对象的prototype添加接口中的成员实现,这一点和CTP版本中在类的构造函数中添加成员定义不同。

Comment  这的确是一种正确的做法。在使用以前的Atlas定义接口和抽象类时可以发现,即使不定义抽象的成员,也能正常工作。如果在产品环境下保留这些代码只是一种浪费。在Microsoft ASP.NET AJAX的Client FX中,提供了一种“标准”以供接口和抽象类遵循,这是一个改进。
另外,文档中没有提到抽象类的定义方法——其实和定一个接口很相似,只是提供部分成员的实现而已。这就基本上使所谓“抽象”类和普通类几乎没有任何区别了,大概也就是因为这样,Client FX去除了所谓的“抽象类”吧。


Enums and Flags

这部分的示例表现了在CTP版本和RTM版本中开发人员创建枚举(Enum)和标记(Flag)的区别。下面的示例展示了在CTP版本中创建枚举的方式:
Type.createEnum("MyEnum""One"1"two"2);

在RTM版本中定义枚举和标记使用了类似的概念,而且增加可用性,并且能够为它添加文档注释。
MyEnum = function() {
   
/// <summary>..</summary>
}
MyEnum.prototype 
= {
  One: 
1,
  Two: 
2
}
MyEnum.registerEnum(
"MyEnum");


Properties, Methods, and Events

在CTP版本中,属性模型的定义是使用“get_”和“set_”来提供get和set方法来表示一个成员。这个模型在RTM版本中依旧保留,只是就像之前所提到的一样,这些方法被转移到了类型的prototype里,而不是使用closure的方式提供定义。

类似地,方法也使用了prototype进行定义。

在Client FX中存在着两种事件:ASP.NET AJAX客户端对象事件和DOM元素上的事件。Atlas的客户端对象将DOM事件封装在自己的事件中,以此为页面开发人员提供新的使用方式。从下面的示例里可能看到这一点:组件将DOM元素的click事件进行了封装,而直接将自己的click事件暴露在对象实例中。

在CTP版本中,客户端对象的事件被作为一个类的成员变量,在构造函数中被定义。页面开发人员能够响应这些对象的事件,在DOM元素的事件被触发时,对象的事件就会被触发。客户端对象在初始化(见示例)时将自己的handler绑定到DOM元素的事件上。因此,也需要在dispose方法中取消对DOM元素事件的绑定。示例如下:
Custom.Button = function(...) {
    
this.click = this.createEvent();

    
this.dispose = function() {
        
if (_clickHandler) {
            
this.element.detachEvent('onclick', _clickHandler);
            _clickHandler 
= null;
        }
        
        Custom.Button.callBaseMethod(
this, 'dispose');
    }
    
    
this.initialize = function() {
        Custom.Button.callBaseMethod(
this, 'initialize');
        
        _clickHandler 
= Function.createDelegate(thisthis._onClick);
        
this.element.attachEvent('onclick', _clickHandler);
    }
    
    
this._onClick = function() {
        
this.click.invoke(this, Sys.EventArgs.Empty);
        ...
    }
}

这个例子也展示了使用attachEvent来相应DOM元素click事件的模型。这和IE的模型非常接近。

在代码中使用自己的handler响应客户端对象事件,会使用如下类似的方式:
// Simple global handler.
var b = new Custom.Button();
b.click.add(OnClickHandler);
function OnClickHandler() {
    ...
}

由于多种原因,这个设计在RTM版本中被改变了。例如为了改善性能,提供和.NET Framework相似的编程模型,遵循ECMAScript标准和基于用户反馈等等。在RTM版本中,您能简单地使用命名规则来定义事件,这和属性的定义有些相似,使用了“add_”和“remove_”方法。工具也能识别出这种命名方式,例如这样就能够在IntelliSense中得到事件的提示了。

在高级的组件中,你能简单地将事件handler加到内置的EventHandlerList对象中,该对象可以使用Compoennt基类的get_events()方法获得。和CTP版本不同的是,RTM版本只在添加事件handler的时候构造事件对象,而不是在构造对象实例时就初始化了所有的事件对象。下面的示例展示了在RTM版本中定义事件的模型:
Custom.Button.prototype = {
    initialize: 
function() {
        Custom.Button.callBaseMethod(
this, 'initialize');
        
this._handler = 
            Function.createDelegate(
thisthis._onClickHandler);
        $addHandler(
this.get_element(), 'click', this._handler);
        ..
    }
    add_click: 
function(handler) {
        
this.get_events().addHandler('click', handler);
    },
    remove_click: 
function(handler) {
        
this.get_events().removeHandler('click', handler);
    },
    dispose: 
function() {
        
// Potential for dispose to be called more than once
        if (this._handler) {
            $removeHandler(
this.get_element(), 'click', this._handler);
            
delete this._handler;
        }
        Custom.Button.callBaseMethod(
this, 'dispose');
    },
    _onClickHandler: 
function() {
        
var ev = this.get_events().getHandler(this._handler);
        
if (ev) {
            ev (
this, Sys.EventArgs.Empty);
        }
    }
}

Comment  似乎Microsoft ASP.NET AJAX的确在性能和编程模型上作了很大努力,这从事件模型的改变上就能看出来。可以发现,“EventHanderList”对象的使用,与.NET Framework里为一个类定义大量事件时所用的Practice如出一辙。

   这个设计使得对象在需要时才会创建事件对象,它使用了一个新的类“DomEvent”的静态方法来添加或删除绑定在DOM元素上的事件,这是一个标准模型。在这个模型之下,一组抽象的API会提供对于浏览器兼容的支持。

如果需要将自己的handler绑定到事件上,您现在可以使用类似于下面的代码。参数sender是可选的,它能让你获得有关事件的更多信息,例如您可以确定是哪个Button被点击了。
var b = new Custom.Button();
b.add_click(OnClickHandler);
function OnClickHandler(sender, args) {
    ..
}

Comment  最后的代码的参数和说明我做了一点修改,个人认为文档上出现里一点错误。