Le forum (ô combien francophone) des utilisateurs de Powerbuilder.
Je trace un bout de code que je viens de faire (PB11.5.1.4843), et je tombe sur ce cas d'objet null et en même temps non-nul, comme le chat de Schröedinger qui est mort et vivant tant qu'on n'a pas ouvert la boîte :
En gros le debugger sait me dire dans le tooltip que mon objet est null (parce que j'ai atteint le dernier parent), mais la méthode isnull() du pbscript ne sait pas le voir...
Du coup ça plante sur un null object au tour de boucle suivant, alors que je teste le cas !
Heureusement je peux m'en sortir en testant en même temps isnull() et isvalid(), qu'on a wrappé ici dans une méthode issafe() pour alléger le code.
Des fois, PB c'est lourdingue
Dernière modification par seki (29-05-2013 10:57:47)
Hors ligne
c'est le genre de truc qui fait
it's null or void
Hors ligne
Salut,
Par rapport à la documentation de PB pour moi tout fonctionne correctement.
La fonction isnull attend le paramètre suivant : A variable or expression that you want to test to determine whether its value is null
La fonction isValid attend le paramètre suivant : An object variable or a variable of type Any—typically a reference to an object that you are testing for validity
Lorsque tu teste isnull (objet ) il ne teste pas la valeur du pointeur mais son contenu hors le pointeur est null donc null object référence
Le test a effectuer sur un objet est toujours isValid et non pas isNull
cdt
Yanis
Hors ligne
Yanis a écrit:
Salut,
...
Le test a effectuer sur un objet est toujours isValid et non pas isNull
cdt
Yanis
attention tout de même à ne pas écrire çà:
if NOT isValid( lpo_obj ) then Messagebox("lpo_obj", "null object reference!" ) else //En fait on passe ici dans le cas où l'objet est NULL //car IsValid() retourne NULL, et NOT NULL est NULL, interprété comme FALSE par le statement IF d'où le passage dans le bloc ELSE. Messagebox("lpo_obj", lpo_obj.classname() ) end if
C'est pour çà que je préfère toujours mixer Isnull et IsValid sur les objets.
if isNull( object ) ornot isvalid(object) then //on ne peut pas utiliser object else //on peut utiliser l'object end if
Petit truc "rigolo" en passant:
powerobject lpo_null boolean lb_test_not, lb_test_or, lb_test_and setnull(lpo_null) lb_test_not = NOT Isvalid( lpo_null ) // lb_test_not = NULL lb_test_or = Isvalid( lpo_null ) OR true // lb_test_or = true lb_test_and = Isvalid( lpo_null ) AND true // lb_test_and = NULL
Donc attention quand on écrit nos tests en PB, le null est contagieux mais pas toujours (cas d'un OR).
Hors ligne
Comme le dit lui même son inventeur le Null c'est vraiment la "billion dollar mistake" (erreur à un milliard de dollars)
Le NULL c'est nul
Hors ligne
Salut,
XLAT a écrit:
if NOT isValid( lpo_obj ) then
Messagebox("lpo_obj", "null object reference!" )
else
//En fait on passe ici dans le cas où l'objet est NULL
//car IsValid() retourne NULL, et NOT NULL est NULL, interprété comme FALSE par le statement IF d'où le passage dans le bloc ELSE.
Messagebox("lpo_obj", lpo_obj.classname() )
end if
J'ai réeffectué un test parce que celà me semblait anormal et je peux te confirmer que je ne passe pas dans le else même si l'objet est NULL (je dirais plutôt non instancié).
Cdt
Yanis
Hors ligne
attention, "non instantié" <> null
Hors ligne
attention, "non instantié" <> null
tu peux tester ca:
powerobject lpo_obj setnull( lpo_obj ) //ok, si on commente cette ligne, l'objet n'est pas valide (isValid retourne false et pas null ) if NOT isValid( lpo_obj ) then Messagebox("lpo_obj", "null object reference!" ) else //En fait on passe ici dans le cas où l'objet est NULL //car IsValid() retourne NULL, et NOT NULL est NULL, interprété comme FALSE par le statement IF d'où le passage dans le bloc ELSE. Messagebox("lpo_obj", lpo_obj.classname() ) end if
Hors ligne
Je sais mais pour un objet Powerbuilder il est instancié ou non instancié, il n'est jamais NULL ou non NULL ( C'était une précision par rapport à ton commentaire ). Une propriété de l'objet est NULL ou non NULL ( variable d'instance ou shared ) sauf s'il s'agit d'une propriété de type objet powerbuilder exemple une variable d'instance de type window.
Cdt
Yanis
Hors ligne
pour compléter:
string ls_str1, ls_str2
powerobject lpo_obj1, lpo_obj2, lpo_obj3
any la_val1, la_val2, la_val3, la_val4, la_val5
setNull(lpo_obj2)
setNull(ls_str2)
setNull(la_val2)
la_val3 = ls_str2
la_val4 = lpo_obj1
lpo_obj3 = GetApplication()
la_val5 = lpo_obj2
Varname isNull? isValid? -------- ------- -------- ls_str1 false n/a ls_str2 true n/a lpo_obj1 false false lpo_obj2 true null lpo_obj3 false true la_val1 false false la_val2 true null la_val3 true null la_val4 false false la_val5 true null
Hors ligne
Yanis a écrit:
Je sais mais pour un objet Powerbuilder il est instancié ou non instancié, il n'est jamais NULL ou non NULL ( C'était une précision par rapport à ton commentaire ). Une propriété de l'objet est NULL ou non NULL ( variable d'instance ou shared ) sauf s'il s'agit d'une propriété de type objet powerbuilder exemple une variable d'instance de type window.
Cdt
Yanis
bah non:
powerobject a, b, c setnull(b) c = create n_object //a n'est pas instantié, n'est pas null et n'est pas valide //b n'est pas instantié, EST null et n'est pas valide (dans le sens que isvalid retourne autre chose que true) //c est instantié, n'est pas null et est valide
Hors ligne
Je ne vois pas l'intérêt de faire un setnull sur un objet Powerbuilder et d'ailleurs un setNull sur un objet autoinstancied est interdit.
Pour moi un objet PB c'est create et destroy mais jamais setnull car c'est la mise à NULL du pointeur mais pas de suppression de l'objet.
Hors ligne
Yanis a écrit:
Je ne vois pas l'intérêt de faire un setnull sur un objet Powerbuilder et d'ailleurs un setNull sur un objet autoinstancied est interdit.
Pour moi un objet PB c'est create et destroy mais jamais setnull car c'est la mise à NULL du pointeur mais pas de suppression de l'objet.
Attention, PB est un langage avec Create + Destroy mais aussi doté d'un ramasse-miettes (garbage collector), comme le C++.
Setnull() sur un objet permet (lire "peut permettre") de libérer une référence à un objet qui peut avoir été créé ailleurs, et qu'on ne veut plus conserver localement. Si jamais c'était la dernière référence active, l'objet est supprimé automatiquement par le garbage collector, même sans appeler de destroy explicite.
L'autoinstancied ça permet juste de ne pas être obligé de créer explicitement (en gros ça existe dès la ligne qui déclare une variable de ce type).
Exemple avec un objet nv_obj dans lequel je trace seulement un message lors des constructor et destructor : je le crée dans un button::clicked, mais ne le détruis pas explicitement :
nv_obj obj debug_message(classname()+"::clicked", "avant création") obj = create nv_obj debug_message(classname()+"::clicked", "fin de l'event") return
Et je vois :
debug view a écrit:
[11700] [cb_gc::clicked] avant création
[11700] [nv_obj::constructor] création
[11700] [cb_gc::clicked] fin de l'event
[11700] [nv_obj::destructor] destruction
L'objet est détruit, mais pas par moi. La mise à null du pointeur vers l'objet (la variable de son type) déclenche la destruction de l'objet (si c'était la dernière).
Hors ligne
Bonjour,
Personnellement, pour annuler une référence à un objet, je préfère coder :
PowerObject lpo_Reset
Objet = lpo_Reset
Plutôt que :
SetNull(Objet)
Du coup je ne teste presque jamais IsNull(Objet) mais toujours IsValid(Objet), un objet n'a pas à être null à mon sens.
J'ai un objet invalide en global qui remplace le lpo_Reset de l'exemple, ce qui me permet de ne pas redéclarer à chaque fois.
Pour les autoInstanciate, c'est une autre histoire mais je n'en utilise que très rarement, je les vois comme des structures auxquelles on peut ajouter des fonctions, peu utile en fin de compte.
Le DESTROY, lui, est très utile car même si PowerBuilder est sensé détruire tout seul les objets inutilisés, il ne le fait pas toujours bien, et de façon peu prévisible, notamment quand il y a des références croisées.
Ex : Objet1 a une variable d'instance Objet2, Objet2 a une variable d'instance Objet1.
A moins de faire les destroy ou de déclencher manuellement le GarbageCollect(), il y a de fortes chances que les objets ne soient jamais détruit.
Cdt
Hors ligne
Salut,
Merci Powerdestroyer. tu as très bien résumé ce que j'essayé de dire, sauf que moi j'utilise toujours le DESTROY.
Cdt
Yanis
Hors ligne
PowerDestroyer a écrit:
..., un objet n'a pas à être null à mon sens. ...Cdt
Si je fais ces tests sur isNull, c'est que des fonctions de la VM peuvent nous retourner des objets null, notamment celles d'introspections (FindClassDefinition, ...), c'est aussi que je fais aussi du c++, java, perl ou la notion d'objet null existe (par contre pas celle d'un objet invalide mais pas null, sauf c++ avec un pointeur "défectueux")
Yanis a écrit:
Je ne vois pas l'intérêt de faire un setnull sur un objet Powerbuilder et d'ailleurs un setNull sur un objet autoinstancied est interdit.
Interdit à la comilation, sinon en runtime ça passe sans lever d'exception, j'avoue que c'est tordu quand même et je crois pas l'avoir déjà utilisé en production.
public function integer setnullai (ref powerobject apow); return setnull( apow ) end function treeviewitem tvi powerobject die_tvi die_tvi = tvi //c'est un powerobject donc passe à la compile (ne fait que déréférencer un autoinstantiated) setnull( die_tvi ) setnullAI( ref tvi ) messagebox( "die_tvi is valid?", string(isvalid( die_tvi ), "[general];null") ) messagebox( "tvi is valid?", string(isvalid( tvi ), "[general];null") )
Sinon, je ne dis pas que c'est mieux de faire un setnull sur un objet plutôt que de changer l'objet référencé par un non instancié, c'est plus pour mettre en évidence les "possibilités" de la VM.
Hors ligne
PowerDestroyer a écrit:
Le DESTROY, lui, est très utile car même si PowerBuilder est sensé détruire tout seul les objets inutilisés, il ne le fait pas toujours bien, et de façon peu prévisible, notamment quand il y a des références croisées.
Ex : Objet1 a une variable d'instance Objet2, Objet2 a une variable d'instance Objet1.
A moins de faire les destroy ou de déclencher manuellement le GarbageCollect(), il y a de fortes chances que les objets ne soient jamais détruit.
Je viens de tester et je confirme :
- si on crée 2 objets qui peuvent avoir en variable d'instance une référence vers un autre objet et qu'on affecte o1->o2 et o2->o1 les objets ne sont collectés quand ils quittent le scope (= quand on sort du bloc qui contenait la seule référence vers ces objets)
- et même simplement si on affecte o->o (référence cyclique directe), l'objet n'est pas détruit en quittant le scope
Le log montre que tous ces objets qui traînaient encore en mémoire (références croisées, et self-référence) ne sont nettoyés qu'à la fermeture de l'application.
Hors ligne