`

关于as3实现对任何对象进行深度复制的思考

 
阅读更多

通过 ByteArray 可以对数组和 Object 进行深度复制,脚本网上满天飞:

var obj:Object = new Object();

var ba:ByteArray = new ByteArray();
ba.writeObject(obj);
ba.position = 0;
var obj2:Object = ba.readObject();

我就想了,能不能用这种方法对任何对象都进行深度复制呢?

试试这样,一般来说网上说了是对数组和 Object,那么对于其它类型多半都会有问题:

var s:Sprite = new Sprite();

var ba:ByteArray = new ByteArray();
ba.writeObject(s);
ba.position = 0;
var s2:Sprite = ba.readObject();

嗯,果然抛错了:

Exception fault: TypeError: Error #1034: 强制转换类型失败:无法将 Object@128a309 转换为 flash.display.Sprite。

那我们再改一下看看
var s:Sprite = new Sprite();

var ba:ByteArray = new ByteArray();
ba.writeObject(s);
ba.position = 0;
var s2:Sprite = ba.readObject() as Sprite;
运行,等于没改,异常依旧。

好了,不买关子了,直接上个能用的,用全局函数 registerClassAlias 注册一下 Sprite 即可:


//关键就是这句
registerClassAlias("flash.display.Sprite", Sprite);

var s:Sprite = new Sprite();
s.x = 100;

var ba:ByteArray = new ByteArray();
ba.writeObject(s);
ba.position = 0;
var s2:Sprite = ba.readObject() as Sprite;

trace("复制的对象 x : ", s2.x);

s2.x = 200;

trace("源的对象 x : ", s.x, "复制的对象 x : ", s2.x);

我们看看输出了什么:

复制的对象 x :  100
源的对象 x :  100 复制的对象 x :  200

哦也,对象成功的复制出来了,等等,好像还输出了什么,待我看看先:

TypeError: Error #1034: 强制转换类型失败:无法将 Object@12883f9 转换为 flash.media.SoundTransform。
TypeError: Error #1034: 强制转换类型失败:无法将 Object@1288629 转换为 flash.geom.Transform。

我转换的是 Sprite 呀,关这个两个类型啥事?后来想了想,貌似 Sprite 里有 自己的 soundTransform 属性和继承自 DisplayObject 的 transform 属性,那好办呀,把他俩也注册一下不就行了。

可是这样还是很麻烦,毕竟我们要把深度复制的对象用到的类都注册一遍,有没有什么办法可以取出该类包括其父类 import 的所有类呢,这里我们可以用另外的一个全局函数 describeType,这个函数吧一个类或类的实例的所有详细说明都取出作为 xml 对象返回,大家可以看看这个返回的数据就知道了:

var xml:XML = describeType(new Sprite());
trace(xml);


好了我们创建一个函数把该类包括其父类 import 的类都取出来吧:
public static function getAllQualifiedClassName($object:*):Array{
        if($object == null){
                throw new ArgumentError("Parameter $object can not be null.");
                return null;
        }else{
                var xml:XML = describeType($object), dictionary:Dictionary = new Dictionary(), i:int, j:int, result:Array = new Array();
                dictionary[xml.@name] = true;
                for(i=0; i<xml.extendsClass.length(); i++){
                        dictionary[String(xml.extendsClass[i].@type)] = true;
                }
                for(i=0; i<xml.implementsInterface.length(); i++)
                        dictionary[String(xml.implementsInterface[i].@type)] = true;
                for(i=0; i<xml.accessor.length(); i++)
                        dictionary[String(xml.accessor[i].@type)] = true;
                for(i=0; i<xml.method.length(); i++){
                        dictionary[String(xml.method[i].@returnType)] = true;
                        for(j=0; j<xml.method[i].parameter.length(); j++)
                                dictionary[String(xml.method[i].parameter[j].@type)] = true;
                }
                for(var obj:* in dictionary)
                        if(String(obj) != "void" && String(obj) != "*")
                                result.push(String(obj));
                return result;
        }
}
考虑到会有很多地方用到就把它静态了,注意 void 和 * 需要剔除,我们取到了所有需要注册的类,接下来就是把他们一并注册了事,嘿嘿嘿:


var allClassName:Array = HObjectUtils.getAllQualifiedClassName(new Sprite());
for each(var item:String in allClassName)
        registerClassAlias(item, Class(getDefinitionByName(item)));

var s:Sprite = new Sprite();
s.x = 100;

var ba:ByteArray = new ByteArray();
ba.writeObject(s);
ba.position = 0;
var s2:Sprite = ba.readObject() as Sprite;

trace("复制的对象 x : ", s2.x);

s2.x = 200;

trace("源的对象 x : ", s.x, "复制的对象 x : ", s2.x);

可是没想到一运行fp就给了我当头一棒:

Exception fault: ArgumentError: Error #1063: flash.geom::Transform() 的参数数量不匹配。应该有 1 个,当前为 0 个。

没想到 ByteArray 的 writeObject 和 readObject 对构造函数带必填参数的对象有先天性的支持缺陷,也就是说如果你要深度复制的对象或它的一个属性指向的对象的构造函数是需要参数的话,不好意思了, writeObject 和 readObject 可没办法传参数,这个方法将会失效,但是我可不甘心,如果说不支持带参数的构造函数的话,我就试试不带参数的情况下这个方法走不走得通,至于构造函数带有参数的深度复制可以寻找其它的路径解决。

这里我弄了个例子:

深度复制类:ObjectUtils

package {
        import flash.utils.getQualifiedClassName;
        import flash.utils.Dictionary;
        import flash.utils.describeType;
        import flash.utils.getDefinitionByName;
        import flash.net.registerClassAlias;
        import flash.utils.ByteArray;

        public class ObjectUtils {
               
                public static function cloneObject($object:*):*{
                        if($object == null){
                                throw new ArgumentError("Parameter $object can not be null.");
                                return null;
                        }else{
                                //为了深度复制一个对象, 需要注册该对象用到的所有类
                                var allClassName:Array = getAllQualifiedClassName($object);
                                for each(var item:String in allClassName)
                                        registerClassAlias(item, Class(getDefinitionByName(item)));
                                //使用 byteArray 对象进行深度复制即可
                                var bytes:ByteArray = new ByteArray();
                                bytes.writeObject($object);
                                bytes.position = 0;
                                //取出复制出的新对象,如果有带参数的构造函数而导致复制失败的就返回 null
                                var className:String = getQualifiedClassName($object);
                                var ObjectClass:Class = Class(getDefinitionByName(className));
                                var result:*;
                                try{
                                        result = bytes.readObject() as ObjectClass;
                                        return result;
                                }catch($error:ArgumentError){
                                        trace($error.message);
                                }
                                return null;
                        }
                }
               
                public static function getAllQualifiedClassName($object:*):Array{
                        if($object == null){
                                throw new ArgumentError("Parameter $object can not be null.");
                                return null;
                        }else{
                                var xml:XML = describeType($object), dictionary:Dictionary = new Dictionary(), i:int, j:int, result:Array = new Array();
                                dictionary[xml.@name] = true;
                                for(i=0; i<xml.extendsClass.length(); i++){
                                        dictionary[String(xml.extendsClass[i].@type)] = true;
                                }
                                for(i=0; i<xml.implementsInterface.length(); i++)
                                        dictionary[String(xml.implementsInterface[i].@type)] = true;
                                for(i=0; i<xml.accessor.length(); i++)
                                        dictionary[String(xml.accessor[i].@type)] = true;
                                for(i=0; i<xml.method.length(); i++){
                                        dictionary[String(xml.method[i].@returnType)] = true;
                                        for(j=0; j<xml.method[i].parameter.length(); j++)
                                                dictionary[String(xml.method[i].parameter[j].@type)] = true;
                                }
                                for(var obj:* in dictionary)
                                        if(String(obj) != "void" && String(obj) != "*")
                                                result.push(String(obj));
                                return result;
                        }
                }
               
        }
}
人物类:Person

package {
        import flash.utils.ByteArray;

        public class Person {
               
                private var _name:Name = new Name();
                private var _age:int = 1;
               
                private var _bytes:ByteArray;
               
                public function Person(){
                }
               
                public function set name(v:Name):void{
                        _name = v;
                }
                public function get name():Name{
                        return _name;
                }
               
                public function set age(v:int):void{
                        _age = v;
                }
                public function get age():int{
                        return _age;
                }
               
                public function set bytes(v:ByteArray):void{
                        _bytes = v;
                }
                public function get bytes():ByteArray{
                        return _bytes;
                }
               
                public function talk():String{
                        _bytes.position = 0;
                        var dream:String = "";
                        while(_bytes.position<_bytes.length)
                                dream += _bytes.readUTF();
                        return "我叫"+_name.x+_name.m+",我"+_age+"岁,我的梦想是"+dream;
                }
               
        }
}

名字类:Name

package {

        public class Name {
               
                private var _x:String = "";
                private var _m:String = "";
               
                public function Name(){
                }
               
                public function set x(v:String):void{
                        _x = v;
                }
                public function get x():String{
                        return _x;
                }
               
                public function set m(v:String):void{
                        _m = v;
                }
                public function get m():String{
                        return _m;
                }
               
        }
}


主类:Main
package {
        import flash.utils.ByteArray;
        import flash.events.Event;
        import flash.display.Sprite;

        public class Main extends Sprite {
               
                public function Main(){
                        if(stage)
                                init();
                        else
                                addEventListener(Event.ADDED_TO_STAGE, init);
                }
               
                private function init(e:Event=null):void{
                        var a:Person = new Person();
                        var aName:Name = new Name();
                        a.name = aName;
                        a.age = 20;
                        aName.x = "张";
                        aName.m = "三";
                        a.bytes = new ByteArray();
                        a.bytes.writeUTF("娶个媳妇!");
                        trace("源对象:", a.talk());
                       
                        var b:Person = ObjectUtils.cloneObject(a) as Person;
                        trace("深度复制后的对象:", b.talk());
                       
                        trace("复制出的对象改变值了!!!看看复制出的对象的值的改变会否影响到源对象!!!");
                       
                        b.name.x = "李";
                        b.name.m = "四";
                        b.age = 22;
                        b.bytes.position = b.bytes.length;
                        b.bytes.writeUTF("再买栋房子!");
                       
                        trace("源对象:", a.talk());
                        trace("深度复制后的对象:", b.talk());
                }
               
        }
}

我们看看输出:

源对象: 我叫张三,我20岁,我的梦想是娶个媳妇!
深度复制后的对象: 我叫张三,我20岁,我的梦想是娶个媳妇!
复制出的对象改变值了!!!看看复制出的对象的值的改变会否影响到源对象!!!
源对象: 我叫张三,我20岁,我的梦想是娶个媳妇!
深度复制后的对象: 我叫李四,我22岁,我的梦想是娶个媳妇!再买栋房子!

为了测试我特意弄了一个 Name 类又使用了 ByteArray 对象,现在看来一切运行良好,唯一的遗憾就是不能支持构造函数带参数的对象的深度复制,关键是只要需要复制的对象和构造函数带参数的对象拉上哪怕一点点关系也要失败,这样一来该 ObjectUtils 工具类的使用面就大打折扣了。

不知道各位仙家有没有更好的方法真正实现任意对象的深度复制,还有本文如有错误也请不吝指教。

另外一种方法的思考:是否可以用 describeType 取出对象的所有属性后在 new 出一个该对象,再把源对象的值逐一复制到新的对象上,如果有属性是引用了另一个对象就在用同样的方法创建该新对象的,直到所有属性都指向新创建的对象为止,这样我们就可以在 new 该对象时把构造函数的参数添加上去了。

另外说一下,用 ByteArray 复制对象时只复制该对象的 public 属性,如果你有一个私有属性,并且该私有属性是通过一个非 set 函数进行赋值的话,该私有属性的值则不会被复制到新的对象中。


======================================================

 

 

 

 

 

 

 

 

最近有点儿时间,帖点儿小经验给大家.有不妥之处敬请包涵.
用byteArray克隆对象,估计大家大多数都用过.应该是类似下面的实现
01.var copier:ByteArray = new ByteArray();

02.            copier.writeObject(source);

03.            copier.position = 0;

04.            return copier.readObject();
复制代码但对于一个相当复杂的对象,要达到完全克隆,还面临着很多问题.
比如下面这几种情况:
1:属性是自定义类型
2.属性是数组,而且数组元素是自定义类型
3.属性是接口形式,但现实是某种实现
4.属性是Dictionary
等等.不知道大家是否碰到过这样的情况.
所以经过研究,对上面的方法进行了一些拓展.形成了下面这种方式进行克隆.


private static function regtype(tn:String):void {
            if (tn == null || tn == "null" || tn == "int" || tn == "string" || tn == "Number" || tn == "String" || tn == "Boolean" || tn == "Object")return;
            var type:Class;
            try {
                type = getClassByAlias(tn);
            } catch(err:Error) {
            }
            if (type != null)return;
            try {
                type = Class(getDefinitionByName(tn));
            } catch(err:Error) {
                return;
            }
            if (type == null)return;
            registerClassAlias(tn, type);
        }

        private static function registerClass(source:*):void {
            var tn:String = getQualifiedClassName(source);
            regtype(tn);
            if(tn=="Array"||tn=="flash.utils::Dictionary"){
                for(var ele:String in source){
                    registerClass(source[ele]);
                }
            }
            var dxml:XML = describeType(source);
            for each(var acc:XML in dxml.accessor) {
                registerClass(source[acc.@name]);
            }
            for each(var acc1:XML in dxml.variable) {
                registerClass(source[acc1.@name]);
            }
            for each(var acc2:XML in dxml.implementsInterface) {
                regtype(acc2.@type);
            }
            regtype(dxml.extendsClass.@type);
        }

        public static function baseClone(source:*):* {
            registerClass(source);
            var copier:ByteArray = new ByteArray();
            copier.writeObject(source);
            copier.position = 0;
            return copier.readObject();
        }


其中baseClone是入口方法,其余两个做辅助工作.具体原理是:
1.用describeType方法对原始对象进行结构分析
2.把原始对象的复杂属性注册别名
3.递归进行2操作.

分享到:
评论

相关推荐

    对象缓存池AS3实现

    本篇将深入探讨对象缓存池的AS3实现,以及与之相关的工厂方法。 首先,让我们了解对象池的概念。对象池是一种内存管理策略,它预先创建并维护一组对象,当程序需要对象时,可以从池中获取,而不是每次都在运行时...

    AS3实现GPS

    AS3(ActionScript 3)是Adobe Flash Platform的主要编程语言,用于创建互动式内容、应用程序以及网络服务。在AS3中实现GPS功能,主要...如果你想要进一步了解,建议查看源代码并与AS3的Geolocation API进行对照学习。

    C# 使用反射来实现对象的深度复制方法

    本文将详细介绍如何使用反射来实现对象的深度复制。 反射是.NET框架提供的一种强大的工具,它允许程序在运行时检查自身并执行动态操作,如创建对象、调用方法或访问字段。在实现深度复制时,反射可以帮助我们遍历...

    as3.0交换深度的问题.txt

    根据提供的文件信息,我们可以推断出此文档主要讨论的是AS3.0(ActionScript 3.0)中关于“交换深度”(Z-index 或显示顺序)的问题。在Flash和AS3.0中,对象的显示顺序是通过Z-index来控制的,即控制对象在舞台上的...

    AS3实现对图片进行任意形状裁剪的例子

    在本文中,我们将深入探讨如何使用ActionScript 3(AS3)来实现对图片进行任意形状裁剪的功能。ActionScript是Adobe Flash平台的核心编程语言,它广泛用于开发富互联网应用程序,包括交互式图像处理和多媒体内容。 ...

    AS3_显示对象结构图.pdf

    本文将对 AS3 显示对象结构图进行详细的解释,并对其各个组件的功能和使用方法进行介绍。 I. 显示对象的概念 在 Flash Player 9 中,显示对象是指可以在屏幕上显示的对象,例如图形、文本、图片等。显示对象可以是...

    flash actionscript3 as3 DesignPattern面向对象23种设计模式的实现源代码.zip

    AS3支持浅复制和深复制,可以利用clone()方法实现。 6. **适配器模式**:将一个类的接口转换成客户期望的另一个接口。在AS3中,通过类的继承和组合实现,将不兼容的接口转化为可兼容接口。 7. **装饰器模式**:...

    as3实现的连连看源代码

    总结,AS3实现的连连看源代码展示了如何利用AS3进行游戏开发的基本流程,包括图形绘制、事件处理、数据结构和算法的运用。深入学习和理解这一源代码,将有助于开发者更好地掌握AS3语言特性,并为开发其他类型的Flash...

    MP3播放器用as实现

    【MP3播放器用AS实现】的开发是一个涉及ActionScript 3(AS3)编程语言的任务,主要用于构建能够在线播放MP3音频文件的应用程序。AS3是Flash平台的核心语言,常用于创建交互式多媒体内容,如网页上的音频和视频...

    设计模式之代理模式(AS3实现)

    在AS3(ActionScript 3)中实现代理模式,可以帮助我们更好地管理和控制对象的访问,提供额外的功能,如缓存、监控、权限控制等。在FlashDevelop这样的AS3集成开发环境中,我们可以方便地编写和测试这种模式。 首先...

    AS3 简单接口实现

    在ActionScript 3 (AS3)中,接口(Interface)是一种定义特定方法集合的蓝图,它强制类去实现这些方法。接口是面向对象编程中的一个重要概念,它允许我们定义一个对象必须实现的方法,但不涉及具体的实现细节。AS3中...

    as3实现的图文混排组件

    在AS3中,我们可以通过TextEvent类监听文本域的“copy”和“paste”事件,然后使用System类的clipboardData属性进行数据的读写,实现对文本和图片的复制粘贴操作。 4. **布局管理**:为了实现图文的灵活布局,我们...

    AS3 实现发邮件

    AS3提供了一种发送电子邮件的方式,这在开发网页游戏、富互联网应用程序(RIA)或任何需要与用户进行实时通信的项目中非常有用。 首先,我们要明确一点,AS3本身并不直接支持发送邮件,它没有内置的邮件服务器功能...

    flash as3 3d立体墙

    以下是关于“Flash AS3 3D立体墙”的详细知识点: 1. **ActionScript 3 (AS3)**: AS3是Adobe Flash中的编程语言,相比于AS2,它具有更高效、类型更严格的语法。AS3引入了类和对象的概念,提供了事件驱动的编程模型...

    as3帮助文档

    6. **网络编程** - AS3支持XMLSocket、URLLoader等类,可用于与服务器进行数据交换,实现XML或JSON的发送和接收。 7. **错误处理** - AS3提供了try-catch语句来捕获和处理运行时错误,这对于编写健壮的应用程序非常...

    Flash AS3实现多浏览器兼容复制按钮

    众所周知,在网页中若想...而对于浏览器来说,Flash不存在兼容性问题,因此我们可以通过ActionScript来实现复制: System.setClipboard("你要复制的内容"); 而网页中要做的是显示并通过flashvars将内容传给Flash。

    as3场景中MC复制一个或多个

    说明:这个是用来实现已在场景中的Mc复制多个出来,如AS2中可以duplicateMovieClip,AS3中用是用New和addChild的, 有人会说As3可以用  方法1——反射方法: var ClassRef:Class = getDefinitionByName...

    Java2As3_Java_对象_转换_AS3_对象

    Java2As3.fxp 是Java2As3.exe的许可文件 Java2As3.exe 是windows 安装包,如果不用工程跑,可先安装AdobeAIRInstaller,再安装Java2As3.exe。 Java2As3.p12是源代码,使用方式: 在eclipse 中 File&gt;&gt;&gt;Import&gt;&gt;&gt;...

    纯as3写的贪吃蛇游戏源码。比较简单……

    本文将围绕一个纯AS3编写的贪吃蛇游戏源码进行深度剖析,帮助读者理解游戏的实现机制,进一步提升AS3编程技能。 贪吃蛇游戏,作为一款经典的游戏,其简单易懂的规则和高度可玩性使其深受玩家喜爱。这款游戏的核心...

    as3实现纸的折叠效果

    在本文中,我们将深入探讨如何使用ActionScript 3 (AS3) 在Adobe Flash CS4环境中实现纸张的折叠效果。这种特效在交互式设计、游戏开发以及动态视觉表现中非常常见,可以为用户带来独特的视觉体验。 首先,AS3是...

Global site tag (gtag.js) - Google Analytics