Bonjour,
Pour éviter les références circulaires, je pense créer une interface pour un Thread.
Exemple:
Y voyez-vous des problèmes potentiels ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part MonThread = class(TThread, iMonThread)
Bonjour,
Pour éviter les références circulaires, je pense créer une interface pour un Thread.
Exemple:
Y voyez-vous des problèmes potentiels ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part MonThread = class(TThread, iMonThread)
non, c'est une des trois méthodes que je propose
http://tothpaul.free.fr/tips.php?pv_circular
personnellement j'ai cependant une préférence pour l'ancêtre virtuel
Merci Paul, je vais regarder de prés ta remarque sur l'ancêtre virtuel !
Par contre, la solution que je n'aime pas c'est le CAST, qui rend le code infernal a lire quand les classes sont trop complexes/imbriqués !
Le seul problème que j'ai rencontré avec l'interface, c'est qu'il a fallu que je déclare ces fonctions la dans mon thread:
Et les implémentant comme ceci:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 // Pour lier l'interface avec le Thread. function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall;
J’espère que cela est propre, car c'est la première fois que j'ai du toucher à ces fonctions.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 function TMonThread._AddRef: Integer; begin // Le comptage de références n'est pas pris en charge. Result := -1; end; function TMonThread._Release: Integer; begin // Le comptage de références n'est pas pris en charge. Result := -1; end; function TMonThread.QueryInterface(const IID: TGUID; out Obj): HResult; begin if GetInterface(IID, Obj) then Result := S_OK else Result := E_NOINTERFACE; end;
Tu peux aussi faire un héritage d'un TInterfacedObject et encapsuler le Thread en interne.
A toi de voir comment gérer le cycle de vie du thread.
Sinon, quel cas extrême de référence circulaire peut amener ce genre de choix technique ?
J'utilise les interfaces pour des cas précis, pour Pattern principalement, du couplage faible, ou quelques rares cas où l'utilisateur du compteur de référence raccourci le code appelant.
Et pour simplifier le code, je découperais
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 type TInterfacedThread = class(TThread, IInterface) protected function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; end;
Code : Sélectionner tout - Visualiser dans une fenêtre à part TMonThread = class(TInterfacedThread, IMonThread)
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !![]()
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
alors sous Delphi, les Interfaces sont basées sur COM, tu as donc toujours au minimum les 3 fonctions en question
QueryInterface qui permet de retrouver une interface à partir d'un GUID...c'est ce qui est appelé quand tu utilises l'opérateur "as" par exemple dans "Obj as IMachinChose"
AddRef et Release c'est le compteur de référence (comme ARC, comme pour les Strings), tu es libres d'en faire ce que tu veux, mais AddRef est invoqué à chaque fois qu'on fait une copie de l'interface, Release à chaque fois que la copie n'est plus utilisée (on lui affecte nil, ou la variable sort de la portée comme par exemple une variable locale quand on quitte une procédure). C'est prévu pour incrémenter un compteur sur AddRef, le décrémenter sur Release et détruire l'objet quand le compteur tombe à zéro. C'est ce que fait TInterfacedObject.
dans ton cas je pense que tu veux surtout une interface au sens plus général, c'est à dire une liste de méthodes disponibles dans un objet, donc ton implémentation est correcte, et la durée de vie de ton objet est à ta charge.
Note qu'avec un ancêtre virtuel tu n'as pas cette gestion Add/Release qui tend à alourdir légèrement le code...Delphi ajoute des try/finally automatiques quand il est nécessaire d'appeler ces méthodes.
Tant que tu es dans les threads...dernièrement j'ai préférer créer une class TTask avec les méthodes Execute et Submit...Execute lance la tâche, Submit lance la tâche depuis un Thread...ça me permet de débuguer facilement en mono thread et d'utiliser mes tâches dans une appli console par exemple, et de mettre en place le thread pour une utilisation en tâche de fond pour une application graphique.
Merci de vos retours, j’ai finalement opté (après tests) pour l’ancêtre virtuel qui répond parfaitement à ma problématique
Ccela m’a l’air très intéressant ton idée de Task Paul, tu as prévu dans faire un article ?
Partager