• as3 反射


    什么是反射
    反射 (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

    package
    {
    public dynamic class Dummy extends Object
    {
    
    public var variable1:String;
    public function Dummy () {
    variable1 = "我是字符串";
    
    // 使用下面 2 个句子分别会报告编译时错误和运行时错误
    // setPropertyIsEnumerable("variable1");
    // super.setPropertyIsEnumerable("accessorOfVar1");
    }
    
    public function get accessorOfVar1 ():String {
    return "通过访问器访问, variable1 : " + variable1;
    }
    
    }
    }
    
    ReflectionSample.as
    package {
    import flash.display.Sprite;
    
    
    public class ReflectionSample extends Sprite
    {
    public function ReflectionSample () {
    testPropsEnumeration();
    }
    
    
    
    /**
    * 测试 for..in 遍历
    * 
    */
    private function testPropsEnumeration ():void {
    trace("测试 for..in 循环, 遍历 Dummy 的实例");
    var dummy:Dummy = new Dummy();
    
    for (var i:String in dummy)
    trace( i + " : " + dummy[i] );
    }
    }
    }
    

      


    最后测试 ReflectionSample, 记得用 debug 模式. 控制台中只会出现

    QUOTE:
    测试 for..in 循环, 遍历 Dummy 的实例
    显然 dummy 中的元素都没有被遍历出

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



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

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

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

    参数  name:String - 类名称
    返回  Object - 返回参数 name 中指定的类引用
    错误  ReferenceError - 找不到参数 name 对应的公共定义
    使用方法如下 :
    获取类 flash.text.TextField 的引用. as 语句是无异常的类型转换. 如果转换失败那么目标变量将被设置成 null

    CODE:
    [Copy to clipboard]
    var ClassReference:Class = getDefinitionByName("flash.text.TextField") as Class;
    实例化所引用的类, 并设置一些属性

    CODE:
    [Copy to clipboard]
    var instance:TextField = new ClassReference() as TextField;
    instance.autoSize = "left";
    instance.text = "我通过 getDefinitionByName 动态创建";
    最后添加到场景中并显示

    CODE:
    [Copy to clipboard]
    addChild(instance);
    修改后的 ReflectionSample.as

    package {
    import flash.display.Sprite;
    import flash.utils.getDefinitionByName;
    import flash.text.TextField;
    
    public class ReflectionSample extends Sprite
    {
    public function ReflectionSample () {
    getDefinitionByNameSample();
    }
    
    /**
    * 使用 flash.utils.getDefinitionByName 动态获取类 (Class) 并创建实例
    * 
    */
    private function getDefinitionByNameSample ():void {
    var ClassReference:Class = getDefinitionByName("flash.text.TextField") as Class;
    var instance:TextField = new ClassReference() as TextField;
    instance.autoSize = "left";
    instance.text = "我通过 getDefinitionByName 动态创建";
    addChild(instance);
    }
    
    }
    }
    

      


    动态获取类名称, 超类 (Superclass) 名称
    有点像之前版本中的 typeof, 这个方法返回的是字符串

    QUOTE:
    public function getQualifiedClassName(value:*):String
    返回类的完全限定名 (fully qualified class name, qualified 我不知道怎么翻了..)

    参数  value:* - 想要得到完全限定名的对象. 他可以是任何 ActionScript 类型, 对象实例, 简单类型如 uint 以及类类型.  
    返回  String - 包含类的完全限定名的字符串


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

    参数  value:* - 任何值.  
    返回  String - 基类的完全限定名, 如果没有的话返回 null
    例子 :
    实例化新的 Sprite, 然后获取他的类名并输出

    CODE:
    [Copy to clipboard]
    var sprite1:Sprite = new Sprite();
    var classNameOfSprite:String = getQualifiedClassName(Sprite);
    trace("Sprite 的类名 : " + classNameOfSprite); // Sprite 的类名 : flash.display::Sprite
    超类

    CODE:
    [Copy to clipboard]
    var superclassNameOfSprite:String = getQualifiedSuperclassName(Sprite);
    trace("Sprite 的超类 (基类) 类名 : " + superclassNameOfSprite); // Sprite 的超类 (基类) 类名 : flash.display::DisplayObjectContainer
    根据刚刚获取的类名使用 创建实例

    CODE:
    [Copy to clipboard]
    var SpriteClass:Class = getDefinitionByName(classNameOfSprite) as Class;
    var sprite2:Sprite = new SpriteClass() as Sprite;
    trace("sprite2 通过 getDefinitionByName 创建 Sprite 实例");
    画一个 100 x 100 的矩形并显示

    CODE:
    [Copy to clipboard]
    sprite2.graphics.beginFill(0xFF00FF);
    sprite2.graphics.drawRect(0, 0, 100, 100);
    sprite2.graphics.endFill();
    addChild(sprite2);
    修改后的 ReflectionSample.as

    package {
    import flash.display.Sprite;
    import flash.utils.getDefinitionByName;
    import flash.utils.getQualifiedClassName;
    import flash.utils.getQualifiedSuperclassName;
    
    public class ReflectionSample extends Sprite
    {
    public function ReflectionSample () {
    getClassNameSample();
    }
    
    /**    
    * 使用 flash.utils.getQualifiedClassName 和 getQualifiedSuperclassName 获取类名称, 并动态创建该类
    * 
    */        
    private function getClassNameSample ():void {
    var sprite1:Sprite = new Sprite();
    var classNameOfSprite:String = getQualifiedClassName(Sprite);
    trace("Sprite 的类名 : " + classNameOfSprite);
    
    var superclassNameOfSprite:String = getQualifiedSuperclassName(Sprite);
    trace("Sprite 的超类 (基类) 类名 : " + superclassNameOfSprite);
    
    var SpriteClass:Class = getDefinitionByName(classNameOfSprite) as Class;
    var sprite2:Sprite = new SpriteClass() as Sprite;
    trace("sprite2 通过 getDefinitionByName 创建 Sprite 实例");
    
    sprite2.graphics.beginFill(0xFF00FF);
    sprite2.graphics.drawRect(0, 0, 100, 100);
    sprite2.graphics.endFill();
    addChild(sprite2);
    }
    
    
    }
    }
    

      



    获取类信息

    QUOTE:
    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 :

    CODE:
    [Copy to clipboard]
    trace(describeType(Sprite);
    返回 :
    ...

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

    我们再试一下获取实例的信息 :

    CODE:
    [Copy to clipboard]
    var sprite1:Sprite = new Sprite();
    var instanceInfo:XML = describeType(sprite1);
    返回 :
    ...

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


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

    实例化

    CODE:
    [Copy to clipboard]
    var dummy:Dummy = new Dummy();
    获取实例信息

    CODE:
    [Copy to clipboard]
    var instanceInfo:XML = describeType(dummy);
    把所有的属性取出来, 包括访问器和变量, 并且访问器不可为只写

    CODE:
    [Copy to clipboard]
    var properties:XMLList = instanceInfo..accessor.(@access != "writeonly") + instanceInfo..variable;
    此时 properties 里就有所有的属性名了. 最后我们遍历这个 XMLList

    CODE:
    [Copy to clipboard]
    for each (var propertyInfo:XML in properties) {

    // 取出属性名
    var propertyName:String = propertyInfo.@name;

    // 根据属性名来访问
    trace(dummy[propertyName]);
    }
    这样 dummy 就被 "遍历" 出了~

    完整代码:

    package
    {
    import flash.utils.describeType;
    import flash.utils.getQualifiedClassName;
    import flash.utils.getDefinitionByName;
    
    /**
    * 使用 flash.utils.describeType 获取类详细信息并缓存
    * @author kakera
    * 
    */
    public class TypeDescriptor
    {
    
    /**
    * 缓存已经反射过了的对象
    */
    private static var typeCache:Object = new Object();
    
    
    
    /**
    * 获取类信息
    * @param target 要获取的目标对象
    * @return TypeDescriptorEntry 实例
    * 
    */
    public static function describeType (target:*):TypeDescription {
    
    var className:String = getQualifiedClassName(target);            
    
    /**
    * 检查缓存中是否已经有目标对象项, 如果有就返回缓存中的内容
    */
    if (className in typeCache)
    return typeCache[className];
    
    
    /**
    * 暂存属性列表
    */
    var propertyNames:Array = [];
    
    
    /**
    * 获取类信息, 如果传入的是实例则获取实例类型的类信息
    */
    var typeInfo:XML = flash.utils.describeType(target is Class ? target : getDefinitionByName(className) as Class);
    
    
    /**
    * 获取类中所有的属性和访问器
    */
    var properties:XMLList = typeInfo.factory..accessor.(@access != "writeonly") + typeInfo..variable;
    
    
    /**
    * 遍历并存放到 propertyNames 中
    */
    for each (var propertyInfo:XML in properties)
    propertyNames.push(propertyInfo.@name);
    
    
    /**
    * 创建 TypeDescriptorEntry 的实例并把 propertyNames 等属性丢进去
    */
    var entry:TypeDescription = new TypeDescription();
    entry.name = className;
    entry.properties = propertyNames;
    entry.typeInfo = typeInfo;
    
    
    /**
    * 缓存到 typeCache 中以便下次从缓存中读取
    */
    typeCache[className] = entry;
    
    return entry;
    }
    
    /**
    * 静态类无须实例化
    * @return 
    * 
    */
    public function TypeDescriptor () { }
    }
    }
    
    这样我们可以专门写一个类, 负责获取类信息, 详细见类中注释
    TypeDescriptor.as, 负责获取信息
    package
    {
    
    public class TypeDescription
    {
    
    /**
    * 类的属性列表, 包括访问器 (accessor) 和变量 (variable)
    */
    public var properties:Array; /* of String */
    
    
    /**
    * 类名
    */
    public var name:String;
    
    
    
    /**
    * 由 flash.utils.describeType() 获取的原始 XML 数据
    */
    public var typeInfo:XML;
    
    
    public function TypeDescription () { }
    }
    }
    

      

  • 相关阅读:
    B站崩溃的背后,b站高可用架构到底是怎么样的?
    批量查询注册表键值函数 RegQueryMultipleValues 应用一例
    windows服务程序的安装和卸载函数
    API 获得GetLastError()错误代码对应的文字信息
    API 在屏幕上简单显示字符串
    API 实现类似于 C# DateTime 的类
    Windows API ReportEvent 写系统日志
    .net core 新增对DOCKER后报 ERR_EMPTY_RESPONSE
    Windows docker 安装报 WSL 2 installation is incomplete.
    SSD固态硬盘装系统无法进入引导
  • 原文地址:https://www.cnblogs.com/tankaixiong/p/2969232.html
Copyright © 2020-2023  润新知