11
votes

Utilisation de conteneurs génériques à Delphi Xe - toujours?

Les conteneurs génériques peuvent être un économiseur de temps lors d'un article et une liste fortement dactylographiée de ces objets. Il enregistre le codage répétitif de la création d'une nouvelle classe avec peut-être une variable interne tlist et des méthodes de type ADD / suppression saisies, entre autres avantages (telles que toutes les nouvelles fonctionnalités fournies par les classes de conteneurs génériques.)

Toutefois, il est-il recommandé d'utiliser toujours des conteneurs génériques pour des listes fortement tapagées à l'avenir? Quels sont les inconvénients spécifiques de le faire? (Si ce n'est pas inquiet pour la compatibilité à l'envers du code.) J'écris une demande de serveur hier et j'avais une liste d'articles que j'ai créée «l'ancienne» et allait la remplacer par une liste générique, mais décidait de le garder maigre, mais surtout hors d'habitude. (Devrions-nous casser l'habitude et commencer une nouvelle en utilisant toujours des génériques?)


2 commentaires

À partir des réponses reçues, c'est un exemple pourquoi Stackoverflow va si bien!


L'utilisation de génériques n'engage également une réduction de vitesse de construction comme indiqué ici: Stackoverflow.com/a/24672687/35696


7 Réponses :


12
votes

dans Delphi Xe, il n'y a aucune raison de ne pas utiliser de conteneurs génériques.

La commutation de l'ancienne méthode avec la coulée vous donnera:

  • Nettoyant, Safe-Safe, Moins Code Pétoné,
  • Enumerators, pour les boucles,
  • la même de meilleures caractéristiques de performance.

12 commentaires

Pas les mêmes caractéristiques de performance, de meilleures performances, car vous n'avez plus le godeter factice qui corrige le type pour le retour. Et les énumérateurs étaient le "tueur" pour moi. Suffit de dire que j'ai maintenant beaucoup de cours qui ressemblent à ce TsomethingList = classe (TOBExtlist ) : Ce sont des cours qui existaient une fois comme des descendants écrits à la main de Tobject Mais maintenant transformé en conteneurs de génériques afin que je puisse utiliser le pour dans boucle.


@Cosmin Je ne suis pas sûr que l'amélioration moyenne des performances est réelle. Getter ne prend pas tellement de temps, et il est toujours préférable d'utiliser une variable locale pour accéder à plusieurs propriétés à un article indexé dans une boucle, par exemple. Alors que tout le code dupliqué généré par des génériques (voir la réponse maçon) pourrait rendre le programme plus lent: le cache de la CPU sera rempli de tout ce code inutile.


Je conviens que les conteneurs génériques peuvent augmenter les performances, car les versions Delphi qui prennent en charge des génériques ont une tdictionnaire qui est probablement le conteneur le plus rapide que vous puissiez obtenir si vous devez trouver un élément dans une liste de la valeur clé.


@Linas Vous pouvez utiliser une fonctionnalité tdictionnaire comme sans générique, même avec des "anciennes" versions DELPHI - voir synopse.info/forum/viewtopic.php?pid=1610#P1610


@ A.bouchez exemple intéressant mais avec des génériques c'est beaucoup plus simple et plus propre imo. Et votre tableau dynamique utilise une recherche binaire (si j'appuie correctement) qui est plus lente que la recherche de dictionnaire.


@Linas Generics n'utilisez pas beaucoup dans une version plus ancienne Delphi. Je suis sûr que si XE était le seul Delphi existant, @ A.bouchez utiliserait toujours un résilary plutôt que le code qu'il lié.


@David c'est vrai. XE s'est amélioré grandement lorsqu'il s'agit de génériques. Mais c'est un autre sujet.


+1 Merci gars! J'allais marquer cela comme la réponse, mais je n'étais pas vraiment convaincu que ... Puis j'ai lu la réponse Deltics.


@Linas sur la vitesse, vous avez raison, du pov mathématique. TDictionary utilise une approche classique de hachage + modulo pour sa recherche. La fenêtre de hachage augmente par une puissance de deux. Selon la fonction HASH utilisée (le hachage par défaut pour un objet a renvoyé son adresse mémoire, il est donc loin d'être loin d'un hachage parfait), sa vitesse peut varier. Ce n'est jamais vraiment o (1) mais ce sera plus rapide que O (n) la recherche binaire dans la plupart des cas. Avec la dépense de la plus grande utilisation de la mémoire. Et si le hash est le cas par défaut (c'est-à-dire son adresse mémoire), vous aurez beaucoup de collisions à l'OMHO. Donc, cela pourrait être plus lent.


@Darian Le piège dans votre réponse acceptée est en réalité une limitation de la connaissance de l'auteur de la réponse. Ce n'est pas un vrai problème. Je suis désolé que vous ayez été séduit par cet argument.


Notez que ce n'est pas seulement un exemple de Deltics, Mason avait un lien d'article dans son commentaire. J'essaie d'utiliser des génériques dans un nouveau développement, mais dans le message original, j'ai mentionné avoir juste écrit du code avec les méthodes standard de TLIST et de sécurité ajoutées et j'ai eu le désir de simplement remplacer tout simplement une liste générique..qui j'ai fait Faites de certaines unités, mais il faut ensuite vous demander quel genre de problèmes je pourrais créer. En allant de l'avant, je cesserai d'utiliser mon outil d'IDE Handy-Dandy qui génère un code de tables en sécurité dans quelques claviers.


Un autre clou sur les génériques Performance Mythe Coffin: Inlinge est désactivé pour les génériques. IMHO ne prétendez jamais que quelque chose est plus rapide sans mesurer réellement (ou au moins regarder le code ASM)



4
votes

devrions-nous casser l'habitude et commencer une nouvelle en utilisant toujours des génériques? oui


3 commentaires

+1 pour répondre spécifiquement et concis de la question indiquée. :)


-1 pour ne pas répondre à l'aspect de la question posée sur les inconvénients. ;)


@ Deltics - d'une manière ou d'une autre, Robert montrait l'un des inconvénients.



3
votes

Dans la plupart des cas, oui, des conteneurs génériques sont une bonne chose. Cependant, Le compilateur génère beaucoup de code en double, et malheureusement La lieur ne sait pas comment le supprimer pour le moment, une utilisation si forte de génériques pourrait entraîner une exécutable gonflée. Mais autre que cela, ils sont géniaux.


2 commentaires

Les modèles d'utilisation les plus courants n'entraînent pas beaucoup de gonflement. Rien de tel que celui de la nouvelle RTTI produit.


@ Mason Wheeler - Il s'agit d'un fichier EXE ballonné ou d'une diminution de la vitesse? Si c'est juste la taille exe que ce n'est pas un gros problème.



1
votes

Vous avez écrit sur la compatibilité en arrière ... C'est ma plus grande préoccupation, si (comme moi), vous écrivez des bibliothèques qui devraient mieux compiler avec les versions les plus courantes de Delphi.

Même si vous utilisez uniquement XE pour un projet fermé, vous faites probablement des bibliothèques personnalisées de votre choix, même si vous ne publiez jamais le code. Nous avons tous de telles unités préférées à portée de main, juste disponibles pour ne pas réinventer la roue de chaque projet.

Dans une mission future, vous devrez peut-être conserver un code plus ancien, sans possibilité de passer à une version plus récente Delphi (pas d'argent pour la migration et la révision des 1 000 000 lignes de code). Dans ce cas, vous pouvez manquer vos bibliothèques XE uniquement, avec des listes génériques brillantes ...

Mais pour une application 100% " privée ", si vous êtes sûr que vous n'aurez jamais à maintenir le code Delphi ancien, je ne vois aucune raison de ne pas utiliser de génériques. Mon seul problème est le problème du code dupliqué (comme cité par Mason): le cache de la CPU peut être rempli de code inutile, la vitesse d'exécution pourrait donc souffrir. Mais dans la vraie application, je pense que vous ne verrez aucune différence.

note : je viens d'ajouter quelque chose Nouveau Caractéristiques de mon wrapper Tdynamnarray . J'ai essayé de mimiquer Le code exemple de l'EMB DOCWIKI . Donc, vous pourriez avoir des caractéristiques de type générique, avec de bonnes anciennes versions Delphes ... Bien sûr, les génériques sont mieux pour travailler avec des cours, mais avec des tableaux et des enregistrements, il ne fait que basculer!


3 commentaires

+1 J'ai initialement déclaré ne pas m'inquiéter de la compatibilité à l'envers, mais vous devez savoir bien sûr ... Je dois soutenir les applications Delphi 5, les applications Delphi 7 Apps, les applications Delphi 2006, les applications Delphi 2007 et les applications Delphi Xe ... I ' m en mission de tout convertir à XE.


A.Bouchez, vous êtes un "cas spécial" car vous écrivez des bibliothèques pour que d'autres utilises, je comprends parfaitement votre désir d'utiliser des constructions de langue compatibles à l'envers. Mais cela ne s'applique pas au reste de nous: le moment où nous ouvrons la case delphi (n + 1) Nous savons que nous allons utiliser des fonctionnalités non disponibles dans Delphi (n) - c'est pourquoi nous avons acheté delphi (n + 1)


@COSMIN - Les bibliothèques d'écriture sont un "cas pire" pour la compatibilité en arrière, vous avez raison. Mais il y a des entreprises qui maintiennent plusieurs logiciels, sur plusieurs versions de Delphi. Et la migration vers un nouveau Delphi n'est pas toujours possible pour des raisons techniques (composantes tierces) ou un budget commercial (coûts de migration, notamment dans une version Delphi Unicode). Donc, vous pourriez avoir, comme l'avou de Darian, plusieurs versions de Delphi toujours en vie dans la même entreprise et à votre ordinateur.



1
votes

Si vous avez besoin de listes polymoples, les génériques sont un obstacle, pas une aide. Cela ne compile même pas, par exemple, parce que vous ne pouvez pas utiliser une tdoglist où un tanimalliste est requis:

  uses
    Generics.Collections;

  type
    TAnimal = class
    end;

    TDog = class(TAnimal)
    end;

    TAnimalList = TList<TAnimal>;
    TDogList = TList<TDog>;


  procedure FeedTheAnimals(const aList: TAnimalList);
  begin
    // Blah blah blah
  end;


  var
    dogs: TDogList;
  begin
    dogs := TDogList.Create;
    try
      FeedTheAnimals(dogs);

    finally
      dogs.Free;
    end;
  end;


7 commentaires

@Darian: Si vous vous demandiez des principes «clairs mais contre-intuitifs» qui conservent l'exemple de travail, j'ai écrit un article sur un moment. Tech.turbu-rpg.com/149/Generics-et- the-covariance-problème


@Deltics, votre exemple est une limitation de la valeur par défaut en génériques.Collections, ce n'est pas une limitation intrinsèque des génériques. Voir ma réponse pour un exemple d'alimentation de différents types d'animaux génériques avec une seule routine!


-1 Il est trivialement facile d'éviter ce prétendu piège avec une utilisation correcte des contraintes. Je suis surpris de quelqu'un aussi expérimenté que vous n'êtes pas au courant de cela.


@David, comment résout-vous cela avec "Utilisation correcte des contraintes"? Je ne pense pas que les contraintes seules suffisent ... Vous avez toujours le problème d'héritage incompatible ( tlist hérite essentiellement de Tobject )


@David - Je n'ai pas appris à manipuler les nouveaux outils de serrage de chat générique pour me permettre de cuire les chats dont j'ai besoin. Je suis beaucoup trop occupé aux chats cutanés de manière efficace, efficacement et de manière productive à l'aide de manière très efficace et beaucoup plus facile à apprendre et à enseigner aux outils de cinglage des chats pour apprendre des connaissances licenciées ou à consacrer le temps à l'enseignement des connaissances redondantes à d'autres personnes qui doivent travailler dans le même installation de cinglé chat.


Je dirais que ce n'est pas que nous ne voulons pas apprendre, c'est une chose à coûts-avantages. Les coûts potentiels de l'utilisation de génériques sur tout, car je pensais pouvoir passer à des listes simples que vous sortiez quotidiennement, semble l'emporter sur les avantages immédiats de le faire. À long terme, je devrais accepter que cela a du sens.


@Darin, les génériques paient surtout pour une liste simple que vous sortez quotidiennement. Avec des génériques, vous déclarez simplement la liste et l'utiliser, rien d'autre. Et si le problème de la liste polymorphe vous inquiète, vous pouvez toujours dériver une nouvelle liste de la part générique à l'aide de techniques "classiques", vous êtes donc du côté de sécurité avec cela aussi. Ne pas mentionner que vous pouvez toujours revenir de génériques à «manuel», car vous ne remplaçez que une ligne de code pour le faire! À mon avis, la seule raison valable de ne pas utiliser de génériques est la compatibilité à l'envers.



10
votes

Ceci a été invité par la réponse de Deltic, je voulais fournir un contre-exemple prouvant que vous pouvez utiliser des génériques pour la routine d'alimentation animale. (IE: Liste générique polymorphe)

Première partie de l'arrière-plan: la raison pour laquelle vous pouvez nourrir des animaux génériques à l'aide d'une classe de liste de base générique est parce que vous aurez généralement ce type d'héritage: xxx

Cela ne fonctionne pas avec les génériques car vous aurez normalement ceci: xxx

... et le seul "ancien ancêtre commun" pour les deux listes est Tobject - pas du tout utile. Mais nous pouvons définir un nouveau type de liste générique qui prend deux arguments de classe: A tanimal et un Tspecificanimal , générant une liste de caractères de type de TspeCificanimal Compatible avec une liste générique de Tanimal . Voici la définition de type de base: xxx

en utilisant ceci, nous pouvons faire: xxx

de cette façon à la fois tdoglist et tcatlist hériter réellement Du Tobjectlist , nous avons donc maintenant une liste générique polymorphe!

Voici une application de console complète qui montre ce concept en action. Et cette classe se rend maintenant dans ma classe de classe pour une réutilisation future! xxx


5 commentaires

Je n'ai pas dit que c'était impossible avec des génériques, j'ai dit qu'ils en avaient fait un obstacle. Félicitations: vous et David entre vous avez très effectivement prouvé exactement mon point, merci! ;) -1 parce qu'il ne répond pas du tout à la question. C'est une réponse qui répond à une autre réponse - elle devrait être un commentaire avec un lien avec un blog ou un autre support développé pour votre observation / explication.


@Deltics, je reçois votre point et respectez votre décision de bowvote. Néanmoins, moins j'ai mis en œuvre votre routine d'alimentation animale , utilisant des listes polymorphes génériques dans 79 lignes de code, sans même essayer de le garder court. Et noter que c'est une application de console complète, y compris des tests. Comment les génériques peuvent-ils être un obstacle s'ils m'ont permis de le faire? Et ma décision d'utiliser une autre "réponse" "pour contrer votre exemple est conforme aux pratiques afin que vous ne puissiez pas avoir de code dans les commentaires. Et s'il vous plaît ne tenez pas ma décision de ne pas écrire un blog contre moi!


@Deltics - Il s'agit d'une élaboration de la question initiale. Parfaitement bien dans mon livre.


Upvoting parce que ce code exemple élaboré est très utile pour moi dans mon travail. J'apprécie le temps pris et je veux compenser le bowvote injustifié!


C'est exactement ce que je cherchais. Merci.



2
votes

Dans l'esprit de la réponse de Cosmin, essentiellement une réponse à la réponse de Deltic, voici comment corriger le code de Deltic: xxx

maintenant, vous pouvez écrire: xxx < / pré>


16 commentaires

Tanimallist hérités de TLIST et qui hérite de tobject . Vous ne pouvez pas faire Var L: TLIST : = Tanimallist .Create , ce n'est pas une liste polymorphe.


@Cosmin Il résout le problème de Deltics. J'ai vu votre liste polymorphe. Je n'essayais pas de reproduire ça.


La réponse dedavid deltic était "si vous avez besoin de listes polymorphes, les génériques sont un obstacle" .


@Deltics mais alors son exemple est réellement résolu par le code que je présente, je crois.


@David Je prends le code de Deltic comme un exemple pour le manque apparent de comportement polymorphique dans des conteneurs génériques. C'est un bon exemple pour cela. Toute sa réponse est en fait résumée dans la première phrase, tout le reste est l'exemple.


@COSMIN Une fonction globale va lutter pour atteindre le polymorphisme. Vous solution, tout en très intelligent, est complexe. En réalité, avec des problèmes du monde réel, il s'agit d'un non-problème. Je suis un peu énervé que Deltics a induit en erreur Darian de cette manière avec ce que je vois comme pessimisme sans fondement.


@David, polymorphisme est une propriété de la hiérarchie de la classe, n'a rien à voir avec la fonction globale. Les listes polymorphes sont tout aussi utiles que toute autre forme de polymorphisme OOP, et tout aussi nécessaire. Votre exemple ne résout pas le problème polymorphe. Et mon code "complexe" n'est que de 10 lignes de long! Je ne compte évidemment que la déclaration de classe générique plus la mise en œuvre du ajoutez et getItem méthodes. La complexité d'exécution est celle du "vieux livre" manuelle redéfinition de getter. L'utilisation est aussi simple que d'utiliser tout autre conteneur générique.


@Cosmin Donne-moi l'exemple du monde réel où vous avez besoin de votre solution. Je n'en ai pas un dans mon codebase. Je ne dis pas qu'il n'y a pas de cas où il est important. Mon point est que cela se sent stupide de se priver des avantages des conteneurs génériques pour un inconvénient qui ne vous aurait jamais un impact!


Et je ne pense pas que les deltiques avaient mis en erreur Darian, je pense que Darian cherchait une raison de garder son "vieille façon" de faire des choses. Mais je pense que Deltics était faux: comme prouvé, vous pouvez avoir des conteneurs génériques et polymorphes.


@COSMIN Vous avez probablement raison. Je suis probablement sur-réagissant. Après tout, nous sommes tous adultes et peuvent prendre nos propres décisions.


@David, vous avez raison, c'est un problème qui pourrait ne pas avoir d'impact sur de nombreuses personnes, et vous pouvez même créer la liste polymorphe à l'aide de techniques "Old School" si le besoin est venu, en héritant simplement de TLIST . En fait, je lis un livre sur asp.net en ce moment où le diction de l'auteur polymorphisme est la caractéristique la plus utilisée et la moins utile (très paraphrasé parce que je ne le fais pas avoir le livre avec moi et je ne suis même pas sûr que l'auteur parlait de OOP en général ou de l'ASP.NET Framework). (à suivre...)


Donner un exemple de listes polymorphes utiles nécessite une hiérarchie de classe non triviale avec un minimum de deux niveaux. Je sais avec certitude que j'ai utilisé des listes polymorphes dans mon code Delphi 2010, mais bien sûr, je suis tellement concentré sur la "solution" qu'elle n'a jamais franchi mon esprit pour écrire cette classe générique, j'ai simplement mis en œuvre la solution en utilisant des techniques "vieille école" et évolué. Étant donné qu'un exemple de mon propre code n'est pas utile, voici une constitution d'une: disons que nous avons une routine prenant une liste d'objets TControl. Disons que nous avons une liste de tfradars personnalisés que nous avons créés au moment de l'exécution et que nous devons garder une trace de ...


... fermer la routine qui prend (tlist ) avec (TLIST n'est pas possible; et tmyframe hérite de < code> tcontrol et la routine initiale peut être appliquée à la liste des cadres. Si nous avions une liste polymorphe de tmyframe qui hérite de tlist nous «D être capable d'appeler la routine. Je suis désolé si cela semble encore extrêmement extrait, mais le polymorphisme est en soi« verbeux »et mettant un autre niveau de polymorphisme OOP-ISH sur le dessus en le rend plus verbeux.


J'ai dit que c'était un obstacle - le fait que vous ayez besoin de forcer des solutions non intuitives à un problème qui a une solution intuitive sans générique fait simplement mon point que les génériques rendent les choses plus difficiles (pas impossibles). -1 parce que cela ne répond pas du tout à la question. C'est une réponse à une autre réponse, "fixant" quelque chose qui n'avait pas besoin de "fixation" car le code "cassé" a été fourni pour soutenir une observation, ne faisant pas de cas concret. Votre réfutation à cette observation devrait être un commentaire avec un lien avec un blog ou un autre support développé pour votre observation / explication.


+1 L'inconvénient est principalement la nouveauté des génériques et le temps possible de frapper dans un exemple de deltisme posté, qui ressemble à je le ferais. (Évier de temps = distrait du flux de développement.) Si vous ajoutez une liste rapide d'éléments à une application de serveur dans laquelle vous aurez le plus probablement la seule chose que vous aurez besoin est d'ajouter (x) compter () et peut-être une routine de recherche personnalisée () Ensuite, je pense que c'est mieux (pour moi) de rester avec l'ancienne Standbys jusqu'à ce que ma basebase sur les génériques se développe et qu'ils deviennent moins un problème potentiel. Tout cela fait partie du cycle d'apprentissage itératif cependant et c'est très apprécié!


Pour que je puisse utiliser la solution de Cosmin lorsque j'ai besoin de simples listes d'objets avec un ancêtre commun. Je fais habituellement. Je veux oublier les vieux temps d'utiliser des conteneurs de Tstringlistes pour des objets!