Piège d'interface de Delphi
Maintenant, je connais deux pièges majeurs:
Piège 1. Piège de conversion de type d'interface
a) Vous ne pouvez pas lancer une référence d'objet à une interface qui ne déclare pas le type de cette référence, même si l'objet implémente réellement cette interface (hehe, les avantages sont difficiles à prononcer). b) Lorsqu'une variable d'objet est affectée à une variable d'interface et lorsque la variable d'interface est affectée à la variable d'objet, l'adresse de la variable d'objet a changé, c'est-à-dire qu'elle n'est plus l'objet d'origine, mais pointe vers une adresse d'erreur . Par exemple: i1 = interface
Fonction à faire: booléen;
fin;
Tc1 = classe
Att1: entier;
fin;
Tc2 = classe (tc1, i1)
Att2: entier;
Fonction à faire: booléen;
end; intf1: i1; obj1: tc !; obj2: tc2; obj2: = tc2.create;
Obj1: = obj2.
I1 (obj2) .do;
I1 (OBJ1) .do; La compilation a échoué. Étant donné que le type d'OBJ1 TC1 ne déclare pas que I1 est implémenté, il ne peut pas être converti en I1, même si OBJ1 implémente I1. De plus, si vous convertissez l'objet en interface, puis en retour, il y aura des problèmes. Obj2: = tc2.create; obj2.att1: = 0;
Intf1: = obj2; // correct. OBJ2: = intf1; OBJ2.Att1: = 0; Autrement dit, après avoir converti la référence de l'objet en référence du pointeur, l'adresse change, mais lorsque la référence du pointeur est remise de la référence de l'objet, puis retournée à la référence de l'objet (bug de Delphi?).
Trap 2. Gestion de l'interface à vie
Sur la base de mon bon sens (voici la programmation du bon sens, pas du bon sens de Delphi), je pense que les interfaces ne nécessitent pas de gestion à vie, car les interfaces ne peuvent pas du tout générer des objets réels. Mais Delphi a une fois de plus frappé mon bon sens (hein, pourquoi dites-vous "renouveler"?), Son interface a une durée de vie, et elle doit implémenter les trois méthodes suivantes: Fonction QueryInterface (const iid: tGuid; out obj): hResult; stdcall; fonction _AdDref: entier; Je ne sais donc pas comment implémenter ces trois méthodes. J Si vous ne souhaitez pas implémenter ces trois méthodes vous-même, vous pouvez utiliser TComponent. Étant donné que TComponent a implémenté ces trois méthodes, il peut en être hérité, il n'est donc pas nécessaire de mettre en œuvre ces trois méthodes. Pouvez-vous l'utiliser en toute confiance? La réponse est non. Parce que Delphi secrètement (parce que c'était au-delà de mes attentes) lorsque vous définissez la variable d'interface sur nil. Fonction _INTFCHEAR (VAR DEST: IInterface): Pointer; var P: Pointer; ) ._Release; fin; fin; et qu'avez-vous fait lorsque vous Fonction TCOMPONontt , alors rien ne le fera pas. Qu'est-ce que nous ne travaillons pas en tant qu'objets com, donc il n'y a pas de problème? La réponse est toujours non Une erreur d'accès à l'adresse illégale se produira. Pourquoi? Comme mentionné ci-dessus, lorsque la référence de l'interface est définie sur NIL, _INTFCHEAR sera appelée, et _intfClear appellera la _release de l'objet. Certaines personnes disent que c'est inutile? Obj2: = TC2.Create; TryIntf1: = OBJ2; Intf1.do; Enfin obj2.free; End; le résultat peut toujours être inattendu, ou l'erreur d'accès à l'adresse illégale. Pourquoi? Parce que le compilateur Delphi est intelligent, il pense que vous avez oublié de définir cette référence d'adresse à NIL, vous allez donc vous l'ajouter automatiquement. Comment le résoudre? Méthode 1: Définissez d'abord la référence de l'interface à NIL, puis relâchez l'objet. INTF1: = NIL; OBJ2.Free; Pointer (intf1): = nil; à ce moment, il est équivalent à effacer directement l'adresse et n'appellera pas _intfclear. J'ai tendance à utiliser la deuxième méthode afin que vous n'ayez pas à penser à qui libérer en premier. De plus, dans certains modèles de conception, vous pouvez contenir des références d'interface, et vous ne savez pas quand l'objet référencé sera libéré. Par exemple, considérez le motif composite. TComposite = class (tcomponent, i1) Interlist privé: txContainer; // une classe de conteneur qui stocke la référence d'interface pour "feuille". Procédure publique Ajouter (AINTF: I1); De toute évidence, non, donc les "feuilles" seront définitivement libérées plus tard que cet objet "Objet de synthèse"? Je ne pense pas. Si ce règlement est mandaté, beaucoup de flexibilité sera perdue. Nous pensons donc certainement que lorsque ces références d'interface sont placées nulles, il n'y aura pas de relation avec l'objet d'origine, afin d'éviter les erreurs d'accès à l'adresse illégales après la libération de l'objet. Quel conteneur devriez-vous envisager d'utiliser? tableau? Tlist? TinterfaceList? Tout d'abord, je pense que ce doit être un Tinterfacelist, car ce que nous voulons accueillir, c'est l'interface. Mais lorsqu'il est libre sur lui, cela met toutes les interfaces qu'elle accueille à nul, ce qui est exactement ce que nous ne voulons pas. Ou nous pouvons convertir la référence d'interface qu'il stocke en un pointeur avant libre, puis le définir sur nil. Pour i: = 0 à interlist.Count -1 dopointer (interlist.items [i]): = nil; Ensuite, nous essayons le tableau. Interlist: Array of i1; Les tableaux dynamiques ne doivent pas être publiés. Il semble très utile, mais lorsque le compilateur le libère, il définira toujours chaque élément sur nil, et en tant qu'interface, il y a toujours une possibilité d'erreurs d'accès à l'adresse illégales. Cela peut être fait de cette manière pour i: = bas (arr) à un dopointer (arr) (arr [i]): = nul; Le temps que vous l'utilisez, et vous ne vous souvenez peut-être pas de le faire ou non. Enfin, utilisez Tlist. Cependant, il y a un pointeur dans Tlist, donc lorsque vous devez procéder comme suit: xxx.add (aintf: i1) commencer Interlist.add (pointeur (Aintf)); End; puisque il enregistre à l'origine un pointeur, il n'y a pas Besoin d'un traitement spécial lors de la libération. Cela semble parfait, mais il y a toujours un piège. interlist.add (tc2.create); ou obj2: = tc2.create; interlist.add (obj2); Parce qu'il s'agit d'un pointeur pur, lorsqu'il est converti en une interface, la conversion d'adresse de l'objet référencé à la référence de l'interface n'est pas réalisée (il ne sait pas comment le faire), donc lors de l'appel de la méthode déclarée par l'interface, Il s'agit d'une autre erreur d'accès à l'adresse illégale. C'est la seule façon d'écrire: InterList.add (Pointer (I1 (TC2.Create)); bien que ce soit un peu gênant, c'est la meilleure solution (comme je le sais). Parce que si vous oubliez que le transfert fait une erreur sur le premier appel, il est plus facile de trouver des erreurs (par rapport à l'utilisation du tableau). Je suggère donc: 1. Utilisez Tlist pour gérer les références d'interface. 2. Lors de l'ajout, vous devez transformer l'objet en interface, puis un pointeur. 3. Pour les références d'interface qui ne sont pas gérées à l'aide de TList. Pour référence aux interfaces, vous devez manquer manuellement sur NIL par la méthode suivante: Pointer (intfref): = NIL; En outre, TinterfacedObject implémente également trois méthodes d'Iinterface. Par conséquent, hériter de celui-ci peut également vous éviter de mettre en œuvre ces trois méthodes. Mais sa _release est implémentée comme celle-ci: fonction tointerfacedObject._release: entier; L'objet. C'est ainsi qu'il est mis en œuvre, il apporte plus de problèmes que d'héritage de TComponent. Sauf indication contraire, il n'est pas recommandé. Toutes les discussions sur les interfaces ci-dessus sont limitées aux interfaces au niveau de la langue utilisée en général, et il n'y a pas de discussion sur les interfaces COM +. L'implémentation de ces étranges interfaces de Delphi a beaucoup à voir avec le développement de l'interface com +, qui est le résultat du compromis fait en raison de la charge historique lourde.