Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox



Enumeration avec un for..in sur une instance de classe as3

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Compatible ActionScript 2. Cliquer pour en savoir plus sur les compatibilités.Par ekameleon (Marc Alcaraz), le 30 janvier 2007

Généralité

l'AS3 se veut différent par sa nature de l'Actionscript 1 et 2 : nouvelle virtual machine, basé sur les nouvelles normes l'ECMAScript4 etc. Reste maintenant à s'habituer au différents petits changements que l'on peut rencontrer en codant avec :)

Je vais donc aujourd'hui vous parler d'un cas intéressant qui peut intervenir un jour ou l'autre quand on cherche malgré nous à utiliser certaines fonctionnalités de l'AS1/2 et que forcément celles ci ne réagissent plus du tout de la même manière en AS3.

Voyons donc la différence entre l'utilisation de l'instruction for..in sur une instance de classe AS2 et une instance de classe AS3.

Exemple et démonstration.

Regardons tout de suite un petit exemple en AS2 pour illustrer l'énumération des instances d'une classe

1 - commençons par une petite classe toute simple AS2 :

class test.User
{
 
	public function User( name:String , age:Number )
	{
		this.age = age ;
		this.name = name ;
	}
 
	public var age:Number ;
 
	public var name:String ;
 
	public function speak( msg:String ):Void
	{
		trace(this + " speak : " + msg) ;
	}
 
	public function toString():String
	{
		return "[User " + this.name + "]" ;	
	}
 
}

2 - Dans flash 8 ou MTASC je vais essayer maintenant d'instancier cette classe et d'énumérer les propriétés de l'instance :

import test.User ;
 
var u = new User("eka", 30) ;
 
trace("my user : " + u) ; // my user : [User eka]
 
for (var prop:String in u)
{
	trace(prop + " : " + u[prop]) ;
}

Résultat des courses dans le panneau de sortie ou la console de Log :

my user : [User eka]
age :eka
name : 30

On observe que les propriétés définies dans le constructeur de la classe sont énumérables alors que les méthodes de la classe définies dans le prototype sont protégées et n'apparaissent pas dans le résultat de la boucle for..in. Les propriétés publiques sont donc énumérables alors que les méthodes non (cela marche de la même manière si je déclare les propriétés mais que je n'utilise pas le constructeur pour définir la valeur de celles ci.)

Passons maintenant à une version AS3 de ce même exemple

1 - La classe test.User avec les petites modifications nécessaires mais qui ne vont pas changer grand chose avec l'exemple précédent.

package test
{
 
	public class User
	{
 
		/**
		 * Creates a new User instance.
		 */
		public function User( name:String , age:int)
		{
			this.age = age ;
			this.name = name ;
		}
 
		public var age:int ;
 
		public var name:String ;
 
		public function speak( msg:String ):void
		{
			trace(this + " speak : " + msg) ;
		}
 
		public function toString():String
		{
			return "[User " + this.name + "]" ;	
		}
 
	}
}

2 - Voici la classe main pour tester l'instanciation et l'énumération de cette classe.

package
{
 
	import flash.display.Sprite;
 
	import test.User;
 
	public class TestDynamicEnum extends Sprite
	{
 
		/**
		 * Creates a new TestDynamicEnum instance.
		 */
		public function TestDynamicEnum()
		{
 
			var u:User = new User("eka", 29) ;
 
			for ( var prop:String in u )
			{
				trace( prop + " : " + u[prop] ) ;	
			}
 
  		}	    	
 
	}
 
}

Résultat … rien dans le panneau de sortie ! Disons à ce niveau là que tout semble normal (C'est justement en AS2 qu'il semble y avoir un problème du coup). Nous pouvons nous dire ici que l'objet n'est pas dynamic donc il n'y a pas de raison qu'il soit énumérable. J'essaie dans ce cas de modifier la nature de la classe test.User en la rendant dynamic.

A noter que dans la documentation de Flex2 en cherchant un peu on peut observer des petites phrases d'explication à ce sujet si ne me trompe pas disant en gros qu'une énumération avec un for..in n'est valable qu'avec une classe dynamic.

package test
{
 
	dynamic public class User
	{
 
		/**
		 * Creates a new User instance.
		 */
		public function User( name:String , age:int)
		{
			this.age = age ;
			this.name = name ;
		}
 
		public var age:int ;
 
		public var name:String ;
 
		public function speak( msg:String ):void
		{
			trace(this + " speak : " + msg) ;
		}
 
		public function toString():String
		{
			return "[User " + this.name + "]" ;	
		}
 
	}
}

Résultat… toujours rien dans la console des Logs :)

Je vais essayer maintenant d'utiliser la méthode setPropertyIsEnumerable() du framework AS3 pour transformer une des propriétés de mon instance pour qu'elle apparaisse dans la boucle for..in (en principe cela devrait marcher en modifiant ici les droits en énumération d'une propriété sur un objet).

package
{
 
	import flash.display.Sprite;
 
	import test.User;
 
	public class TestDynamicEnum extends Sprite
	{
 
		/**
		 * Creates a new TestDynamicEnum instance.
		 */
		public function TestDynamicEnum()
		{
 
			var u:User = new User("eka", 29) ;
 
			u.setPropertyIsEnumerable("name", true) ; // name enumerable ?
 
			for ( var prop:String in u )
			{
				trace( prop + " : " + u[prop] ) ;	
			}
 
  		}	    	
 
	}
 
}

Résultat… encore rien lol

Au final j'essaie un dernier test (totalement fou ou pas ?) ! Je vais coder comme en AS1 dans ma classe test.User en ne déclarant pas les propriétés (cela ne devrait pas causer de problème étant donné que la classe est dynamique).

package test
{
 
	dynamic public class User
	{
 
		/**
		 * Creates a new User instance.
		 */
		public function User( name:String , age:int)
		{
			this.age = age ;
			this.name = name ;
		}
 
		public function speak( msg:String ):void
		{
			trace(this + " speak : " + msg) ;
		}
 
		public function toString():String
		{
			return "[User " + this.name + "]" ;	
		}
 
	}
}

Cette fois le résutat est positif et le for..in fonctionne.

age : 29
name : eka

Je vais essayer maintenant de désactiver l'énumération de la propriété name sur mon instance pour voir si la méthode setPropertyIsEnumerable() fonctionne ?

package
{
 
	import flash.display.Sprite;
 
	import test.User;
 
	public class TestDynamicEnum extends Sprite
	{
 
		/**
		 * Creates a new TestDynamicEnum instance.
		 */
		public function TestDynamicEnum()
		{
 
			var u:User = new User("eka", 29) ;
 
			u.setPropertyIsEnumerable("name", false) ;
 
			for ( var prop:String in u )
			{
				trace( prop + " : " + u[prop] ) ;	
			}
 
  		}	    	
 
	}
 
}

C'est bon, cette fois ci la méthode a réussi à modifier les droits d'une propriété de mon instance et seul la propriété age apparaît.

Conclusion

L'AS3 doit considérer que toute propriété, même publique, doit être protégée dans une classe. Je trouve cela totalement normal.

En AS2 et AS1 il était possible d'utiliser la fonction non documenté ASSetPropFlags pour modifier et sécuriser les propriétés.

Maintenant la question est de savoir si il est possible de revenir en arrière sur les protections imposées par l'AS3 et son compilateur. Je n'ai pas vu en AS3 une classe ou une méthode identiques à la fonction globale ASSetPropFlags ActionScript ou à la fonction setAttributes que l'on peut trouver en SSAS ou Javascript par exemple.

J'espère vraiment que Adobe a laissé quelque part des outils pour modifier si on le souhaite les droits des instances.. sinon il va falloir se contenter de l'API de réflexion basée sur l'E4X de l'AS3 pour énumérer le contenu des propriétés et méthodes d'une classe. Je trouve cela peut être un peu pénalisant à première vue pour un framework de Tests Unitaires par exemple ou autre ? Mais je peux me tromper, je manque d'expérience dans l'implémentation de tels outils et il se peut qu'au final la réflexion du code soit préférable qu'une énumération.

En savoir plus

Auteur

Par ALCARAZ Marc (aKa eKameleon) 2007. Vous pouvez retrouver ce tutorial et des commentaires à ce sujet sur mon blog.