[SOLVED] Storing different items as Dynamic and using them as their real classType

Hi, I am instantiating several items and storing them as Dynamic: they are different kinds of “TextField”, that can be extensions of TextField class itself or of MovieClips containing a TextField. All of these classes have a “text” editable property: in TextField extensions it is the default property, but in the extended/nested classes it can be a setter to indirectly edit text property of the contained TextField.

Now the problem is that when I have to set the text property/setter of these instances (that I remind are stored as Dynamic) Haxe does not know how to behave, and if I do

anInstance.text = "DOG";

it doesn’t update the item on screen, maybe because it doesn’t know what to do with the “text” property of a Dynamic: I have to cast it before.
How can I store these items as Dynamic and cast them to the correct class right before using their properties?

Using pseudo-code I could imagine something like this:

tfVariants = Array<Dynamic> = new Array<Dynamic>();
tfVariants.push(new CakeTextField());
cast(tfVariants[0], tfVariants[0].getMeTheRealClassType).text = "DOG";


P.S.: it is funny to notice that if I trace the instance it shows the “[real classType name]”, but the item behaves as a Dynamic item.
If I cast it and trace it it shows the same “[real classype name]” as before, but the item behaves as the set classType.

How about Reflect.setProperty(myDynamicObject, “text”, string); ?

1 Like

@hcwdikk: I have edited the post, please try to see if it is more clear now.

Please can you explain your suggestion? Reflect is still a bad beast for me.

tfVariants = Array<Dynamic> = new Array<Dynamic>();
tfVariants.push(new CakeTextField());
Reflect.setProperty(tfVariants[0], "text", "DOG");

That way you wouldn’t have to use casting and it should work fine on dynamic objects (at least I think so :slight_smile: )

Yes! YES! It works perfectly! Thank you very much @hcwdikk! :laughing:
I have to deepen my knowledge about Reflect…

Much of Haxe’s type system is compile-time only. For instance:

var tf:TextField = new TextField();
tf.text = "Hello, world.";

TextField.text is defined with both a getter and a setter ("public var text (get, set):String;"), so at runtime, the variable text doesn’t exist at all. All that’s left are the get_text() and set_text() functions.

This is where the compiler comes in. When it sees the code above, it makes the necessary replacement:

var tf:TextField = new TextField();
tf.set_text("Hello, world.");

And then it works. But what if the compiler didn’t know what type of object tf was?

var tf:Dynamic = new TextField();
tf.text = "Hello, world.";

Because Dynamic is specified, any value can be assigned to tf; it doesn’t have to be a text field. Now the compiler doesn’t know that text uses a getter+setter, so it doesn’t make any replacement, so the set_text() function never gets called.

But what about casting?

var tf:Dynamic = new TextField();
cast(tf, TextField).text = "Hello, world.";

Now the compiler knows the type, so it makes the correct replacement. But can you see how this is different from your code?

It’s different because the value of “TextField” is known at compile-time (hint: the value is TextField). However, the value of “tfVariants[0].getMeTheRealClassType” isn’t known until runtime. The compiler can’t work with that.

Reflect solves your problem by examining the class for matching getters/setters. That’s the purpose of setProperty() as opposed to setField() - the former is slower, but it finds the set_text() function.

An easier solution would be to call set_text() yourself. That way, the compiler doesn’t have to replace anything, and the correct function will still be called:

var tf:Dynamic = new TextField();
tf.set_text("Hello, world.");

But the best solution is to use a typedef. Using a typedef tells the compiler, “I don’t know exactly what class this object will be, but I do know it has a property named text.” Then the compiler can make the correct substitution when you access text:

typedef TextFieldVariant = {
    var text(get, set):String;
tfVariants:Array<TextFieldVariant> = new Array<TextFieldVariant>();
tfVariants.push(new CakeTextField());
tfVariants[0].text = "DOG";

Easy as that! As a bonus, the compiler can make sure that whatever goes into the array has a text property:

tfVariants.push(9999); //Error!

That’s a really great explanation :slight_smile: I didn’t know typedefs can be used like that.