[AS3, Solved] Adding properties to objects at runtime

27 posts

Flag Post

As the title says, I want the ability to declare new properties for an object while the application is running. I don’t know if that’s even possible but my current best guess:

public function declareInt(name:String):void
{
	var this[name]:int;
}

is highly illegal and doesn’t compile. No need to tell me I’m an idiot for even trying this approach, I know. But then again, what should I be doing instead, in order to do it right?

 
Flag Post

Pretty sure you can assign a property to an Object object whenever you want using the dot notation or the ["property"] notation – perhaps with no possibility of setting the variable type

I know I use this, but I can’t remember exactly how

EDIT: See this post: http://www.kongregate.com/forums/4-game-programming/topics/300249#posts-6386014

As an aside, referencing an undefined property, i.e. myObject.undefinedProperty should return null if I remember correctly

Don’t know if this is relevant, but you may want to research dynamic classes

 
Flag Post

This isn’t exactly what you’re going for, but couldn’t you have declareInt create a new empty object and use its’ .x (or whatever), to store the value, and have a getter that worked out which ‘name’ meant which object?

Edit: okay, ignore that, Dealninja seems more on the money.

 
Flag Post

Make sure your class is typed as dynamic and then just simply go this["propertyname"] = x to create properties on it. If the class is not typed as dynamic, you can still read existing properties (and functions) in the same manner, you just can’t create new ones.

 
Flag Post
Originally posted by Ace_Blue:

As the title says, I want the ability to declare new properties for an object while the application is running.

The object has to be derived from a dynamic class.
By all means, you do not want to make your class dynamic.

Originally posted by Ace_Blue:

what should I be doing instead, in order to do it right?

Either use a generic Object (which is dynamic) as a datastructure:

var o:Object = {};
o["foo"] = 12345;

or use a Dictionary:

var d:Dictionary = new Dictionary();
d["foo"] = 12345;

The difference is that Dictionary can use Objects as keys.

Whether that’s the way to go depends on what you actually want to do.

 
Flag Post

I look at that code and go “var d should have been var dog” and then go “no, that, AND swap it with foo.”

</thread derail>

 
Flag Post

Personally, I’d try to avoid dynamic properties.

 
Flag Post

Extremely not helpful post saying hi.

This is possible through the Reflection namespace in C-based programming. I assume there is something similar in ActionScript.

 
Flag Post

Thanks for all the replies. Dynamic seems to be a complete shoe-in for the problem. However, this:

Originally posted by NineFiveThree:

The object has to be derived from a dynamic class.
By all means, you do not want to make your class dynamic.

is ominous. Why would I not want to make a class dynamic if it solves my problem?

The Adobe reference doesn’t seem to contain any warning about using dynamic classes, and apparently MovieClips are dynamic by default and don’t trigger widespread devastation, so now I’m confused. Could you elaborate?

Edit: OK, so I tried it, and I can see two problems with dynamic classes.
1) No strong typing for dynamic variables. I can do this[toto] = 3; on one line, this.toto = ‘cucumber’; on the next line, and the compiler won’t bat an eyelash. It’s like AS2 all over again and it makes me cringe just seeing those two lines.
2) The compiler will accept any property name for a dynamic object and not care whether they’re defined or not. So if I declare this[banana] but later mistype Banana as the variable name, it will go unmentioned and unnoticed at compilation, and I could potentially spend days tracking down the resulting bug.
Are these issues what you had in mind Nine? Or are there even bigger problems I’m not aware of?

So yea, I don’t think I’ll be using dynamic classes after all. Thanks all the same for the help.

 
Flag Post

Some downsides are lack of static and some dynamic type checking, slower access, awkward shadowing of static properties and the possibility that they won’t be well supported in future versions of AS. If none of that is overwhelmingly important to you, then go for it.

 
Flag Post

Yes, that’s why I suggested making only the datastructure dynamic.

 
Flag Post

You won’t get a compiler error because these references are resolved at runtime. What is it you’re trying to do? There may be another way, if you’re interested.

 
Flag Post

There are most likely simple ways of combating the issues you’ve raised, if you think about how to maintain your dynamic variables

 
Flag Post

Yes BigJM, I fully understand that. What I am doing doing is creating a superclass for my game objects so I can save the state my game is in without trouble. As you most likely know, SharedObject has a lot of trouble handling complex objects, and cannot save the private properties of objects since it can’t access them. Upon loading, it creates a new instance of the loaded object before transferring all the variable values back in, which precludes the constructor from taking any argument which doesn’t include a default value. For all these reasons and more, I want to serialize my game objects for saving, and de-serialize them upon loading, so the SharedObject instance only ever handles ByteArrays, which it does well.

So now, this SavedObject superclass needs to contain two public methods: serialize():ByteArray and deserialize(ByteArray):void to save and load the contents of the object, respectively. But it also needs to know which variables it’s supposed to serialize and what they’re named. This information is of course dependent on exactly what the Class inheriting from SavedObject actually is, so it’s not possible for SavedObject to know it in advance.

But let’s be honest, these variables are overwhelmingly likely to be of one of the following types: uint, int, Number, String, Boolean or SavedObject. So, in order to simplify my task, I added five protected Arrays to the SavedObject definition: persistentInt, persistentNumber, persistentString, persistentBoolean and persistentSavedObject. These are Arrays of Strings, as they store the names of the variables, not their contents. When (de-)serializing, I loop over those Arrays to get all the names of the variables of a given type in need of saving/reading and process them from there. If a subclass of SavedObject ever needs to save something of a different type (an Array, for instance) there is nothing stopping it from overriding the serialize() and deserialize() functions, so I have all the functionality I need.

In order to declare a persistent variable (one which gets saved), all I have to do is put in the declaration and constructor of a descendant of SavedObject the following lines:

var toto:int;

and

persistentInt.push('toto');

respectively.
But that’s TWO lines, and we can’t have that. And that’s how you ended up with this thread. Because I felt that:
1) I should just be able to declare a new variable with its type and have it automatically added to the proper persistent Array, something like a function in SavedObject declare(name:String, type:Class) that would handle all the paperwork.
2) Those persistent Arrays should really be private, not protected, because there is no reason beyond variable declaration for subclasses of SavedObject to ever need them.

This little foray into dynamic classes has cured me from this madness, and I am now willing to accept the extra line as the cost of doing business, but you wanted to know, so now you do. Assuming this wall of text didn’t discourage you. And you also know why I didn’t go into the details of the context in the OP.

All these issues could be solved if only an AS3 object had a way of creating a list of its non-public variables with their types, but it doesn’t and we’ve been over that before.

 
Flag Post

It’s highly likely that I’m not understanding you, but couldn’t you just implement/override the serialize method in whatever class it is your serializing so that you can explicitly write the private variables to the data stream. It doesn’t seem to me like the names of your variables would or should even matter.

 
Flag Post

If I do that, then any subclass of SavedObject will have to have its own version of the (de-)serialize functions, tailored to its particular variables, which completely defeats the purpose of creating the SavedObject class in the first place. The way I have it set up, any subclass that only wants to save uints, ints, Numbers, Booleans, Strings or SavedObjects simply uses the super functions without any more intervention on my part… as long as I’ve marked the variables I want saved in the subclass constructor. It is much more convenient for me, especially since I don’t even have to worry about the order of my class variables anymore. Everything gets read in the order it was written, by construction.

Edit: But I have to make the variables I want saved public or I’ll get a runtime error when the super’s serialize() tries to access properties of the subclass. So this method is still not ideal.

 
Flag Post

Think of the serialize() similar to toString(): it provides a specific description of the object, hence every class has to provide the way how it’s supposed to happen.

 
Flag Post

As I already stated, the idea is to write the serialize function once and have every subclass use it as is unless they have specific needs, in which case they only expand upon it by overriding it and calling the super as the first action of the overriding function.

And it works. So thank you all, you may consider the topic closed and the problem solved.

 
Flag Post
Originally posted by Ace_Blue:


Edit: But I have to make the variables I want saved public

Then what’s the point of your system?
Just register the alias and write the object to a bytearray

 
Flag Post

Originally posted by NineFiveThree:

The object has to be derived from a dynamic class.
By all means, you do not want to make your class dynamic.

dynamic objects have no performance impact unless you reference a property that wasn’t defined at compile time; and if at any point you access an object that does not exist in the hashmap, it constitutes a miss and has an enormous overhead (more than 2x an existing runtime property has)

Originally posted by Ace_Blue:

Edit: But I have to make the variables I want saved public or I’ll get a runtime error when the super’s serialize() tries to access properties of the subclass. So this method is still not ideal.

make them protected, not public:

public function serialize():void {
	var t:Object = this;
	for each (var k:String in persist) {
		save(k, t[k]);
	}
}

superclasses should be able to access protected properties; though not directly (not that it matters in the case of the above)

also, if all you want is an int to pass by reference:

public class Integer {
	public var value:int;
	public function Integer(v:int):void {value = v}
}

of course, without operator overloading you must do intVar.value ...

 
Flag Post

I was not mainly referring to performance.

 
Flag Post
Originally posted by skyboy:

make them protected, not public:

public function serialize():void {
	var t:Object = this;
	for each (var k:String in persist) {
		save(k, t[k]);
	}
}

superclasses should be able to access protected properties; though not directly (not that it matters in the case of the above)

Unfortunately no, it seems that a function of a superclass accesses the properties of the subclass as if it were another object entirely, so protected properties cannot be accessed, and the little gymnastics with declaring an extra Object to act as a stand-in for ‘this’ doesn’t work for me. So to clarify the problem if you really want to solve it (I’ve made peace with the idea that my saved variables will be public, personally)

Make a class A, containing a string depvar:String = ‘mySavedVariable’; and a public function show():void {trace(this[depvar]);}
Make a class B which extends A, containing a protected variable mySavedVariable:Number = 42;
In Main instanciate test:B = new B(); and call test.show();
Blam! Error.

test is a B and therefore contains a property mySavedVariable, intialized and all, and yet test’s own inherited show function cannot see it. Change mySavedVariable to public and all of a sudden it works. Everything happens as if show() is treating ‘this’ as an external object.

 
Flag Post

You can access properties of subclasses from a super class while making them inaccessible to everything else by using a custom namespace and dynamic access.

package {
	public class A {
		protected namespace foo = "foo"
		public function bar():void {
			trace(this.foo::["far"])
		}
	}
}
//
package {
	public class B extends A {
		foo var far:String = "far"
	}
}
//
var b:B = new B()
b.bar()
//trace(b.far) error, inaccessible property
 
Flag Post

Thank you. I suspected a custom namespace might do it, but I wasn’t quite sure how to go about it. This helps a lot. Except now I have to drag along the custom namespace with every reference to any variable it contains, so myVariable becomes foo::myVariable, systematically and everywhere, or the compiler throws a fit… Or is that what the ‘use’ keyword is for?

You’re going to say I’m never happy, but I think I’m just going to stick with public vars.

And adding the simple “use namespace foo;” line at the beginning of each subclass of A allows me not to have to worry about dragging the namesapce identifier anymore. Woo! It finally works as intended.

 
Flag Post

Internally, the namespace is still dragged everywhere. Also, if you don’t use the compiler strict mode you won’t need to use dynamic access.