请选择 进入手机版 | 继续访问电脑版
收藏本站腾讯微博新浪微博
点点网模板设计大赛 phpchina

经典论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

蓝色理想 最新研发动态 用悬赏 三天解决问题 解决访问速度慢 论坛支持农历生日 - 给官方提建议

论坛活动及任务 归纳网站最新活动 地图任务 邮件更新任务:保护帐号安全

积分换实物,来参加蓝色理想积分兑换吧! 联系招聘客服 蓝色理想帮你找工作! 万元奖励等你拿——点点网模板设计大赛

查看: 22532|回复: 15

[AS1&2] AS3 中的反射 (Reflection) [复制链接]

mirycat 楼主

CK同伈鎖

荣誉管理

帖子
4640
体力
19243
威望
372
发表于 2006-7-31 17:06:40 |显示全部楼层
本文主要介绍 as3 中新引入的反射机制
感谢草莓和 yhc13 提供人肉翻译机支持 :D

什么是反射
反射 (Reflection) 是指在程序在运行时 (run-time) 获取类信息的方式. 诸如实现动态创建类实例, 方法等. 在很语言中都有相关的的实现, 如 Java 和 c# 等

反射有什么用
在 as3 与 as2 不同, 类实例中任何元素, 如变量 (variable), 访问器 (accessor, 即 getter / setter), 方法 (method) 都是不可被 for..in 遍历的 (不是默认哦, 目前我还没找到办法可以让他被遍历),
并且不管你是否继承自 Object (默认继承就是 Object, 不写也一样), 是否把类声明为 dynamic.
或许有人会问自然是 Object 的子类, 不是可以用 setPropertyIsEnumerable 来设置是否隐藏变量么.
很遗憾的是经过的我的尝试, 在类里使用 setPropertyIsEnumerable("属性名") 编译器报告方法可能未定义.
随后尝试 super.setPropertyIsEnumerable("属性名"), 编译通过但抛运行时错误, 同样是方法未定义 -_-
而其他方法诸如 propertyIsEnumerable("属性名") 却可以正常使用

新建一个 ActionScript 项目, 分别创建下面 2 个类
Dummy.as

 提示:您可以先修改部分代码再运行



ReflectionSample.as

 提示:您可以先修改部分代码再运行



最后测试 ReflectionSample, 记得用 debug 模式. 控制台中只会出现
测试 for..in 循环, 遍历 Dummy 的实例

显然 dummy 中的元素都没有被遍历出

在 as1, 2 中很简单就可以实现的问题在 as3 得换个办法了, 谁让他们是传统的脚本语言呢
而在 as3 中, 就得通过反射来解决这个问题了. 方法会在文后介绍



动态创建实例
* 这部分内容帮助中已经有例子, 我摘要一些翻译一下, 不过我的 e 文很烂. 希望大家能看得懂.

as3 使用 flash.util.getDefinitionByName 动态获取类 (Class) 引用
帮助中该方法的描述 :

public function getDefinitionByName(name:String):Object
返回参数 name 中指定的类引用

参数  name:String - 类名称
返回  Object - 返回参数 name 中指定的类引用
错误  ReferenceError - 找不到参数 name 对应的公共定义



使用方法如下 :
获取类 flash.text.TextField 的引用. as 语句是无异常的类型转换. 如果转换失败那么目标变量将被设置成 null
  1. var ClassReference:Class = getDefinitionByName("flash.text.TextField") as Class;
复制代码


实例化所引用的类, 并设置一些属性
  1. var instance:TextField = new ClassReference() as TextField;
  2. instance.autoSize = "left";
  3. instance.text = "我通过 getDefinitionByName 动态创建";
复制代码


最后添加到场景中并显示
  1. addChild(instance);
复制代码



修改后的 ReflectionSample.as

 提示:您可以先修改部分代码再运行






动态获取类名称, 超类 (Superclass) 名称

有点像之前版本中的 typeof, 这个方法返回的是字符串
public function getQualifiedClassName(value:*):String
返回类的完全限定名 (fully qualified class name, qualified 我不知道怎么翻了..)

参数  value:* - 想要得到完全限定名的对象. 他可以是任何 ActionScript 类型, 对象实例, 简单类型如 uint 以及类类型.  
返回  String - 包含类的完全限定名的字符串
public function getQualifiedSuperclassName(value:*):String
返回目标对象基类的完全限定名,
本函数提供比 describeType 更简便的方法来获取基类的名称
提示 : 本函数限制只寻找实例的继承层次,而 describeType() 函数使用的是类对象继承.
调用 describeType() 函数时返回的是基于超类以的类继承结构. 而 getQualifiedSuperclassName() 将忽略类的继承结构直接返回最接近的继承对象
例如, 理论上 String 类继承自 Class, 但调用 getQualifiedSuperclassName(String) 时返回的是 Object. 换句话说, 不管你传递的是类还是类的实例, 他们的返回值都是一样的

参数  value:* - 任何值.  
返回  String - 基类的完全限定名, 如果没有的话返回 null



例子 :
实例化新的 Sprite, 然后获取他的类名并输出
  1. var sprite1:Sprite = new Sprite();
  2. var classNameOfSprite:String = getQualifiedClassName(Sprite);
  3. trace("Sprite 的类名 : " + classNameOfSprite); // Sprite 的类名 : flash.display::Sprite
复制代码


超类
  1. var superclassNameOfSprite:String = getQualifiedSuperclassName(Sprite);
  2. trace("Sprite 的超类 (基类) 类名 : " + superclassNameOfSprite); // Sprite 的超类 (基类) 类名 : flash.display::DisplayObjectContainer
复制代码


根据刚刚获取的类名使用 创建实例
  1. var SpriteClass:Class = getDefinitionByName(classNameOfSprite) as Class;
  2. var sprite2:Sprite = new SpriteClass() as Sprite;
  3. trace("sprite2 通过 getDefinitionByName 创建 Sprite 实例");
复制代码


画一个 100 x 100 的矩形并显示
  1. sprite2.graphics.beginFill(0xFF00FF);
  2. sprite2.graphics.drawRect(0, 0, 100, 100);
  3. sprite2.graphics.endFill();
  4. addChild(sprite2);
复制代码



修改后的 ReflectionSample.as

 提示:您可以先修改部分代码再运行







获取类信息

public function describeType(value:*):XML
生成一个 XML 对象来描述参数中指定的 ActionScript 对象, 这个方法使 ActionScript 实现了反射编程的概念.

如果参数 value 是某类的实例, 那么返回的 XML 对象包含了此类中所有的实例属性, 但是不会包含任何的静态属性.
这种情况下你可以通过检查标签 <type> 中的 isStatic 属性来判断他, 当参数为某类的实例时, 这个值为 false.

要获取类的静态属性, 可以通过传递类本身到参数 value, 这样返回的 XML 对象不仅包括了类的静态属性, 也包括所有的实例属性.
实例属性被包含在 <factory> 标签中使它们和静态属性区别开来. 在这种情况下, <type> 标签的 isStatic 属性为 true.

提示: 如果你只需要获取对象的继承结构而不需要 describeType() 提供的其他信息, 可以使用 getQualifiedClassName() 和 getQualifiedSuperclassName() 来替代

下表描述了 describeType() 生成的 XML 的标签和属性 (按运行代码察看)

 提示:您可以先修改部分代码再运行



另外, 由 describeType() 返回的类描述信息中只会含有所有可被访问的元素, 即所有非定义为 private 的元素

解释了这么多, 我们来看看返回的 XML 格式
flash 的内置类 flash.display.Sprite :
  1. trace(describeType(Sprite);
复制代码

返回 :

 提示:您可以先修改部分代码再运行


真是又臭又长啊. :o, 根据帮助中的描述, 所有的实例属性都被嵌套在了 <factory> 标签里

我们再试一下获取实例的信息 :
  1. var sprite1:Sprite = new Sprite();
  2. var instanceInfo:XML = describeType(sprite1);
复制代码

返回 :

 提示:您可以先修改部分代码再运行


所有的标签与之前 factory 标签中的内容是一样的
       

现在我们使用 describeType 来遍历之前 Dummy 类中的元素

实例化
  1. var dummy:Dummy = new Dummy();
复制代码

获取实例信息
  1. var instanceInfo:XML = describeType(dummy);
复制代码

把所有的属性取出来, 包括访问器和变量, 并且访问器不可为只写
  1. var properties:XMLList = instanceInfo..accessor.(@access != "writeonly") + instanceInfo..variable;
复制代码

此时 properties 里就有所有的属性名了. 最后我们遍历这个 XMLList
  1. for each (var propertyInfo:XML in properties) {
  2.        
  3.         // 取出属性名
  4.         var propertyName:String = propertyInfo.@name;
  5.        
  6.         // 根据属性名来访问
  7.         trace(dummy[propertyName]);
  8. }
复制代码


这样 dummy 就被 "遍历" 出了~

完整代码:

 提示:您可以先修改部分代码再运行



这样我们可以专门写一个类, 负责获取类信息, 详细见类中注释
TypeDescriptor.as, 负责获取信息

 提示:您可以先修改部分代码再运行


TypeDescription.as, 负责保存信息

 提示:您可以先修改部分代码再运行


[ 本帖最后由 mirycat 于 2006-12-23 11:20 编辑 ]
附件: 你需要登录才可以下载或查看附件。没有帐号?注册
西部数码顶级域名注册商39元抢注!
帖子
1396
体力
6754
威望
6
居住地
江苏省 苏州市
发表于 2006-7-31 17:43:08 |显示全部楼层
不懂,太深奥了
租服务器,上51IDC | [长沙]招聘:PHP经理10K/WEB前端6K/PHP开发6K

使用道具 举报

绝版

中级会员

帖子
135
体力
384
威望
0
发表于 2006-8-1 17:24:13 |显示全部楼层
好文章
一男对一女说:“我请你吃饭。”
女说:“改日吧。”

使用道具 举报

帖子
12
体力
88
威望
0
发表于 2006-8-7 19:38:37 |显示全部楼层
文章很强,了解 AS 3 本质有很大帮助。

平时用 mx.utils.ObjectUtil.toString 可以遍历 Object 的所有 properties 了。

使用道具 举报

帖子
672
体力
5345
威望
37
发表于 2006-8-23 21:29:50 |显示全部楼层
基本写的不错,大致上涵盖了3个反射机制的方法和x4e的使用方法,但是补充一点:不用反射机制,同样可以遍历类元素。

只要将类对象强制转换为Object型,就可以使用as2.0的for in 方法遍历,虽然只能遍历出public的属性,但是一般情况下也够用了。
成为权威的唯一方法,就是无视一切权威。

使用道具 举报

mirycat 楼主

CK同伈鎖

荣誉管理

帖子
4640
体力
19243
威望
372
发表于 2006-8-23 23:28:34 |显示全部楼层
强制转成 Object? 刚刚试了怎么不行 -_-

Member.as

 提示:您可以先修改部分代码再运行



Temp.as

 提示:您可以先修改部分代码再运行



依然没结果出现, debug 中察看 n 的类型还是 Member,
请指教

使用道具 举报

帖子
672
体力
5345
威望
37
发表于 2006-8-24 09:33:06 |显示全部楼层
首先,很抱歉,前面的言论有点言之过早了。使用强转Object型的方法,可以访问类元素,这点是肯定的,但是没有用for in那么简单(因为这个例子很久以前写的了,细节忘记了,在mirycat的提醒下,我回头找到以前的source看了下,处理source如下)

  1.                 private function testFunc():void{
  2.                         var tt:TestClass = new TestClass();
  3.                        
  4.                         var obj:Object = tt as Object;
  5.                        
  6.                         var arr:Array = getObjectNameList(obj);
  7.                        
  8.                         for (var i:int = 0; i < arr.length; i++){
  9.                                 trace(arr[i] + ":" + obj[arr[i]]);
  10.                         }
  11.                 }
  12.                 public static function getObjectNameList(obj:Object):Array{
  13.                         //get the String [Object@ \n aaa = 123 \n bbb = 234] by an Object
  14.                         var nameList:String = ObjectUtil.toString(obj);
  15.                         //get the array like ['Object@',' aaa = 123 ', ' bbb = 234 ']
  16.                         var arrNames:Array = nameList.split("\n");
  17.                         //check the array has correct result
  18.                         if (arrNames.length > 1){
  19.                                 //delete the element 'Object@' from array
  20.                                 arrNames.shift();
  21.                                 //loop and get all element
  22.                                 var len:int = arrNames.length;
  23.                                 for (var i:int = 0; i < len; i++){
  24.                                         //get the array like [' aaa ', ' 123 '] from array element
  25.                                         var arrKeyValue:Array = arrNames[i].split("=");
  26.                                         //get the trimed String like 'abc' and push it in a array
  27.                                         arrNames[i] = StringUtil.trim(arrKeyValue[0]);
  28.                                 }
  29.                         }else{
  30.                                 //if data not correct , return a empty array
  31.                                 arrNames = new Array();
  32.                         }
  33.                         //return it
  34.                         return arrNames;
  35.                 }
复制代码


上面这个方法除了可以取得类元素外,最关键的功能是可以取得元素定义次序,里面的变量arr是array型,他的次序就是元素被声明的次序,在某些情况下非常有用。

仔细看了下回复,前面一楼的也发现了这个方法。这里汗颜一下。

[ 本帖最后由 sansunzw 于 2006-8-24 09:42 编辑 ]
成为权威的唯一方法,就是无视一切权威。

使用道具 举报

帖子
672
体力
5345
威望
37
发表于 2006-8-24 09:38:19 |显示全部楼层
另外,上面这个方法,已经被我收录到我的AS3工具组件(ASAT)里,当然之外还有很多其他函数和库,包括我前两天发的log4s工具,呵呵。

另外mirycat获得类详细结构的方法,本人深感佩服,只是拿这个方法来遍历类元素,有点大材小用了。

[ 本帖最后由 sansunzw 于 2006-8-24 09:45 编辑 ]
成为权威的唯一方法,就是无视一切权威。

使用道具 举报

mirycat 楼主

CK同伈鎖

荣誉管理

帖子
4640
体力
19243
威望
372
发表于 2006-9-8 00:37:45 |显示全部楼层
哎呀呀. ObjectUtil 是不是 mx.utils.ObjectUtil ? 那他那里面也是用的 describeType. -_-
个人觉得, 如果不是纯 flex 应用的项目, 还是避免使用 flex framework 的类库, 文件好小很多啊...

使用道具 举报

一只小海豹

高级会员

帖子
209
体力
1104
威望
0
发表于 2006-10-15 19:40:30 |显示全部楼层
好贴,下了,好好看
在Web程序的开发中度过每一天......
我Blog:
一只小海豹!

使用道具 举报

帖子
109
体力
444
威望
0
发表于 2006-12-23 09:01:54 |显示全部楼层
檔案無法下載喔!麻煩樓主檢查一下!

使用道具 举报

mirycat 楼主

CK同伈鎖

荣誉管理

帖子
4640
体力
19243
威望
372
发表于 2006-12-23 11:21:17 |显示全部楼层
已修正, 谢谢

使用道具 举报

白骷争宝髅气

金牌会员

帖子
1300
体力
3149
威望
73
居住地
广东省 深圳市
发表于 2007-5-14 15:34:00 |显示全部楼层
原帖由 sansunzw 于 2006-8-23 21:29 发表
基本写的不错,大致上涵盖了3个反射机制的方法和x4e的使用方法,但是补充一点:不用反射机制,同样可以遍历类元素。

只要将类对象强制转换为Object型,就可以使用as2.0的for in 方法遍历,虽然只能遍历出pub ...


三种反射机制分别是哪些啊?偶好笨搞不清耶
<font color="red"><strong>!

使用道具 举报

白骷争宝髅气

金牌会员

帖子
1300
体力
3149
威望
73
居住地
广东省 深圳市
发表于 2007-5-14 16:28:04 |显示全部楼层
给mirycat大人挑点小毛病:



如图,propertyNames.push(propertyInfo.@name);
这样push进去的是XMLList,浏览不方便,直接push字符串好像更好

还有,如果我自己把TypeDescription整个都定义成XML,
name和properties都添加到XML里面不会有什么不好吧?
(新手改高手的东东总是心里不踏实)
附件: 你需要登录才可以下载或查看附件。没有帐号?注册
<font color="red"><strong>!

使用道具 举报

帖子
2013
体力
5137
威望
23
居住地
广东省 广州市
发表于 2010-7-8 10:05:02 |显示全部楼层
好帖 不错 自我增值了一下!!
少壮不努力,老大搞IT!从小不学好,长大编程序! -_-#

使用道具 举报

帖子
2013
体力
5137
威望
23
居住地
广东省 广州市
发表于 2010-7-8 10:23:35 |显示全部楼层
咦 奇怪了 为什么回复了 帖子没有上升显示在块面第1分页的?? 设置了不允许老帖上升吗?
少壮不努力,老大搞IT!从小不学好,长大编程序! -_-#

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

Archiver|手机版|安久科技提供CDN|blueidea.com ( 京ICP备05002321号 )  

GMT+8, 2012-2-11 03:35 , Processed in 0.110801 second(s), 8 queries , Gzip On, Memcache On.

Powered by Discuz! X2

© 2001-2011 Comsenz Inc.

回顶部