Cet article est un article miroir de traduction automatique, veuillez cliquer ici pour accéder à l’article original.

Vue: 20516|Répondre: 0

[Pourboires] Quarante-sept façons d’optimiser un programme C#

[Copié le lien]
Publié sur 15/03/2018 10:41:59 | | |

1. Remplacer les champs accessibles par des attributs

1、. La liaison de données .NET ne supporte que la liaison de données, et vous pouvez bénéficier des avantages de la liaison de données en utilisant des attributs.
2. Dans l’accès get and set à la propriété, vous pouvez utiliser le verrou pour ajouter le support multithreading.

2. lecture seule (constante d’exécution) et const (constante de compilation)

1. la const ne peut être utilisée que pour les types primitifs, les enums et les chaînes, tandis que la lecture seule peut être de n’importe quel type ;
2. Const sera remplacé par une constante spécifique au moment de la compilation, de sorte que si les valeurs const et en lecture seule sont utilisées dans la référence, le passage à lecture seule modifiera l’intention initiale de la conception, qui est la nécessité de recompiler l’assembleur modifié pour reréférencer la nouvelle valeur constante.
3. La const est plus efficace que la lecture seule, mais perd la flexibilité de l’application.

3. IS et AS

1. Les deux sont des conversions de types à l’exécution, car les opérateurs ne peuvent être utilisés que dans des types de référence, tandis que is peut utiliser des valeurs et des types de référence ;
2. La pratique habituelle consiste à utiliser IS pour déterminer le type, puis à choisir d’utiliser comme ou un opérateur de conversion de type fort (conversion définie par un opérateur) de manière sélective.

4. ConditionalAttribute au lieu de #if #endif条件编译

1. ConditionalAttribute n’est utilisé qu’au niveau de la méthode, et d’autres éléments tels que les types, attributs, etc. sont invalides. Et #if #endif则不受此限制 ;
2. ConditionalAttribute peut ajouter plusieurs opérations OR (OR) pour des conditions de compilation, et #if #endif则可以添加与(AND) [ici peut être défini complètement comme un autre symbole séparé] ;
3. La définition de ConditioanlAttribute peut être placée dans une méthode séparée pour rendre le programme plus flexible.

5. Fournir la méthode ToString()

1. Il peut fournir des informations détaillées aux utilisateurs de manière plus conviviale ;
2. Utilisez la méthode IFormatter.ToString() pour offrir une personnalisation plus flexible, et si vous ajoutez les interfaces IFormatProvider et ICustomFormatter, il sera plus logique de personnaliser la sortie du message.

6. La différence entre valeur et type de référence

1. Les types de valeurs ne supportent pas le polymorphisme, qui convient au stockage des données exploitées par les applications, tandis que les références supportent le polymorphisme, qui est adapté à définir le comportement applicatif.
2. Pour les tableaux définis comme des types de valeurs, les performances du programme peuvent être significativement améliorées ;
3. Le type de valeur présente moins de fragmentation de la mémoire du tas, de déchets mémoire et de temps d’accès indirect, et son retour dans la méthode se fait sous forme de réplication afin d’éviter d’exposer la structure interne au monde extérieur.
4. Les types de valeur sont utilisés dans les scénarios suivants : Les responsabilités des types sont principalement utilisées pour le stockage des données ; Les interfaces publiques sont entièrement définies par certains attributs d’accès aux membres des données ; Il n’y a jamais de sous-classes ; Il n’y a jamais de comportement polymorphe.

7. Les types de valeurs doivent être implémentés comme étant constants et les types atomiques autant que possible

1. Rendre notre code plus facile à écrire et à maintenir ;
2. Trois stratégies pour initialiser les constantes : en construction ; méthode des plantes ; Construis une classe assistante mutable (par exemple StringBuilder).

8. S’assurer que 0 mérite un statut valide

1. L’état par défaut du type de valeur doit être 0 ;
2. Le 0 du type énum ne doit pas être invalide ; Dans le FlagsAttribute, il s’agit de s’assurer que la valeur 0 est valide ;
3. Lorsque la chaîne est vide, une chaîne peut être retournée. ficelle vide pour vide.

9. Relations multiples de représentation du jugement égal

1. ReferenceEquals() détermine que les références sont égales, et cela doit être vrai lorsque les deux font référence au même objet.
2. La méthode statique Equals() est utilisée pour établir d’abord un jugement de référence, puis pour juger le type de valeur ;
3. Pour le jugement du type de référence, vous pouvez utiliser la méthode de réécriture Equals() lorsque vous utilisez la sémantique des valeurs.
4. Lors de la réécriture de la méthode Equals(), la méthode GetHashCode() doit également être réécrite, et l’opération opérateur==() doit être fournie en même temps.

10. Comprendre les limites de la méthode GetHashCode()

1. GetHashCode() n’est appliqué qu’aux valeurs de hachage des clés définies ** basées sur le hachage, telles que HashTable ou Dictionary ;
2. GetHashCode() doit suivre les trois règles correspondantes : deux objets égaux doivent retourner le même code de hachage ; doit être un invariant d’instance ; La fonction de hachage doit produire une distribution aléatoire entre tous les entiers.

11. Donner la priorité à l’utilisation des instructions de boucle foreach

1. foreach peut éliminer la vérification par le compilateur de la frontière du tableau de la boucle for ;
2. La variable circulaire de foreach est en lecture seule, et il existe une transformation explicite, qui crée une exception lorsque le type d’objet de l’objet ** est incorrect ;
3. Le ** à utiliser pour chaque type est : avoir la méthode publique GetEnumberator() ; L’interface IEnumberable est explicitement implémentée. L’interface IEnumerator est implémentée ;
4. foreach peut apporter les avantages de la gestion des ressources, car si le compilateur peut déterminer l’interface IDisposable, il peut utiliser l’optimisation try... enfin bloquer ;

12. L’initialisation du champ par défaut est meilleure que l’instruction d’attribution

1. La durée de vie du champ initialisera le type de valeur à 0 et le type de référence à nulle par défaut.
2. Initialiser le même objet plusieurs fois réduira l’efficacité d’exécution du code.
3. Mettre l’initialisation du champ dans le constructeur favorise la gestion des exceptions.

13. Utiliser le constructeur statique pour initialiser les éléments statiques

1. Le constructeur statique sera exécuté avant qu’aucune méthode, variable ou attribut d’une classe ne soit consulté ;
2. Les champs statiques s’exécutent également avant le constructeur statique, et le constructeur statique est propice à la gestion des exceptions.

14. Utiliser la chaîne constructeur (dans NET 4.0 résout déjà ce problème avec des paramètres optionnels)

1. Utiliser cela pour transférer le travail d’initialisation à un autre constructeur, et utiliser la base pour appeler le constructeur de la classe de base ;
2. La séquence d’opérations des instances de type est : fixer tous les champs statiques à 0 ; Exécution d’initialisateurs de champs statiques ; un constructeur statique qui exécute la classe de base ; Des constructeurs statiques qui exécutent le type courant ;
Fixez tous les champs d’instance à 0 ; Exécuter des initialiseurs de champs d’instance ; Exécuter le constructeur d’instance de classe de base approprié ; Exécutez le constructeur d’instance du type actuel.

15. Utiliser les instructions using et try/finally pour nettoyer les ressources

Dans la méthode Dispose() de l’interface IDisposable, GC.SuppressFinalize() peut être utilisé pour notifier le collecteur d’ordures que l’opération finale n’est plus effectuée.

16. Minimiser les déchets de mémoire

1. Il faut un temps supplémentaire au processeur pour allouer et détruire des objets sur un tas ;
2. Techniques pour réduire le nombre d’objets assignés : les variables locales fréquemment utilisées sont promues en champs ; Fournit une classe qui stocke des instances courantes d’objets Singleton exprimant des types spécifiques.
3. Utiliser StringBuilder pour effectuer des opérations complexes de chaînes.

17. Minimiser les emballages et le déballage

1. Prêter attention à la conversion implicite d’un type en System.Object, et le type de valeur ne doit pas être remplacé par le type System.Object ;
2. L’utilisation d’interfaces au lieu de types peut éviter le boxing, c’est-à-dire l’implémentation des types de valeur à partir des interfaces, puis l’appel des membres via des interfaces.

18. Implémenter le mode Dispose standard

1. Pour utiliser des ressources non mémoire, il doit avoir un finalisateur, le collecteur de déchets ajoute les objets finalisateurs implémentés à la file d’attente après avoir terminé les objets mémoire qui ne les ont pas terminés, puis le collecteur lancera un nouveau thread pour exécuter les finalisateurs sur ces objets. Cela peut éviter le problème de fuite de mémoire causé par le manque de libération de ressources mémoire non gérées.
2. Utiliser la méthode IDisposable.Dispose() nécessite quatre aspects du travail : libérer toutes les ressources non gérées ; Libérez toutes les ressources gérées ; Définir un marqueur de statut pour indiquer si Dispose() a été exécuté ; Appelez GC.SuppressFinalize(this) pour annuler l’opération de terminaison de l’objet ;
3. Ajouter une méthode virtuelle protégée Dispose() au type qui nécessite un polymorphisme, et la classe dérivée libère sa tâche en réécrivant cette méthode.
4. Dans le type qui nécessite une interface IDisoposable, nous devrions implémenter un terminator même si nous n’en avons pas besoin.

19. Définir et implémenter des interfaces sur les types d’héritage

1. Les types non liés peuvent implémenter conjointement une interface commune, et il est plus facile d’implémenter une interface que l’héritage ;
2. L’interface est relativement stable, elle encapsule un ensemble de fonctions dans une interface comme d’autres types d’implémentations se contractent, tandis que la classe de base peut être étendue dans le temps.

20. Distinguer entre implémentation d’interface et réécriture de méthodes virtuelles

1. Lors de l’implémentation d’une interface dans la classe de base, la classe dérivée doit utiliser new pour masquer l’utilisation de la méthode de la classe de base ;
2. La méthode de l’interface de classe de base peut être déclarée comme une méthode virtuelle, puis implémentée dans la classe dérivée.

21. Utiliser l’entrust pour exprimer des rappels

1. Le délégué lui-même ne fournit aucune capture d’exception, donc tout appel multicast de délégué met fin à toute la chaîne d’appel.
2. En affichant et appelant chaque cible de délégation sur la chaîne de délégués, vous pouvez éviter que les délégués multicast ne retournent que la sortie du dernier délégué.

22. Utiliser les événements pour définir des interfaces externes

1. Il doit être déclaré comme un événement commun, et laisser le compilateur créer des méthodes add et renmove pour nous.
2. Utiliser le conteneur System.ComponentModel.EventHandlerList pour stocker chaque gestionnaire d’événements, et l’utiliser pour masquer la complexité de tous les événements lorsque le type contient un grand nombre d’événements.

23. Éviter de retourner des références aux objets de classe internes

1. Puisque l’accès à un objet de type valeur crée une copie de l’objet, les attributs de définition d’un type de valeur ne modifient pas du tout l’état à l’intérieur de l’objet type ;
2. Les types constants peuvent éviter de modifier l’état de l’objet ;
3. Définir l’interface pour limiter l’accès à un sous-ensemble afin de minimiser les dommages à l’état interne de l’objet.
4. Définir un objet enveloppant pour restreindre l’accès à un autre objet ;
5. Lorsque le code client modifie les éléments de données internes, le mode Observateur peut être implémenté, afin que l’objet puisse vérifier ou correspondre aux modifications.

24. La programmation déclarative est meilleure que la programmation impérative

La possibilité d’erreurs dans plusieurs algorithmes manuscrits similaires peut être évitée et un code clair et lisible est fourni.

25. Mettre en œuvre les types d’implémentation aussi sérialisables que possible

1. Le type ne représente pas un contrôle d’interface utilisateur, une fenêtre ou un formulaire, et le type doit supporter la sérialisation ;
2. Lors de l’ajout de l’attribut désérialisé de NonSerializedAttribute, la valeur par défaut peut être chargée par la méthode OnDeserialization() qui implémente IDeserializationCallback ;
3. En contrôle de version, vous pouvez utiliser l’interface ISerializable pour un contrôle flexible, et fournir un constructeur de sérialisation pour initialiser les objets selon les données du flux, et également exiger l’autorisation des exceptions SerializationFormatter lors de l’implémentation.
4. Si vous devez créer une classe dérivée, vous devez fournir une méthode d’accroche pour la classe déduite.

26. Utiliser les interfaces IComparable et IComparer pour implémenter les relations de tri

1. L’interface IComparable est utilisée pour implémenter la relation de tri la plus naturelle pour les types, surchargeant quatre opérateurs de comparaison, et fournissant une version surchargée de la méthode CompareTo() pour accepter des types spécifiques comme paramètres.
2. IComparer est utilisé pour fournir des relations de tri différentes d’IComparable, ou pour nous fournir des relations de tri que le type lui-même indique non implémentées.

27. Éviter les interfaces isolables

1. Pour les types de valeurs, il n’est pas nécessaire de supporter l’interface ICloneable, il suffit d’utiliser l’opération d’attribution par défaut ;
2. Pour les classes de base qui peuvent devoir supporter des interfaces ICloneables, un constructeur de réplication protégé doit être créé pour elles, et les interfaces IConeables doivent être évitées.

28. Éviter les opérateurs de conversion forcée

Utiliser des constructeurs au lieu d’opérateurs de conversion peut rendre le travail de conversion plus clair, ce qui peut facilement entraîner des bugs étranges dus à des objets temporaires utilisés après conversion.

29. Ne considérez l’utilisation du nouveau modificateur que lorsque l’accumulation de nouvelles versions pose problème

30. Implémenter autant que possible des assemblages compatibles CLS
1. Pour créer un assembleur compatible, deux règles doivent être suivies : les paramètres et les types de valeurs de retour utilisés par tous les membres publics et protégés de l’assembleur doivent être compatibles avec CLS ; Tout membre public et protégé qui n’est pas compatible avec le CLS doit avoir une alternative compatible avec le CLS ;
2. Vous pouvez contourner la vérification du type de compatibilité CLS en implémentant explicitement l’interface, et l’attribut CLSCompliantNe vérifiera pas la compatibilité CLS des membres privés.

31. Mettre en œuvre une méthode courte et concise autant que possible

1. Le compilateur JIT compile en unités de méthodes, et les méthodes non appelées ne seront pas compilées par JIT ;
2. Si le code de l’instruction Case dans le Switch plus long est remplacé par une méthode à la fois, le temps gagné par le compilateur JIT sera multiplié ;
3. Des méthodes courtes et concises et la sélection de moins de variables locales permettent d’optimiser l’utilisation des registres ;
4. Moins il y a de branches de contrôle dans la méthode, plus il est facile pour le compilateur JIT d’insérer des variables dans les registres.

32. Réaliser autant que possible des assemblages de petite taille et de haute cohésion

1. Mettre toutes les classes publiques et les classes de base commune dans certains assemblies, placer les classes d’outils fournissant des fonctions pour les classes publiques dans le même assembleur, empaqueter les interfaces publiques pertinentes dans leurs propres assemblages, et enfin traiter les classes qui sont présentes à l’horizontale dans l’application ;
2. En principe, deux types de composants doivent être créés : l’un est un petit ensemble agrégé avec une fonction spécifique, et l’autre un ensemble grand et large avec des fonctions communes.

33. Limiter la visibilité des types

1. L’utilisation d’interfaces pour exposer les fonctions des types peut faciliter la création de classes internes sans limiter leur disponibilité en dehors de l’assemblage ;
2. Moins il y a de types publics exposés au monde extérieur, plus vous disposez d’options pour l’expansion future et la mise en œuvre du changement.

34. Créer une API Web à grande échelle

Cela minimise la fréquence et la charge des transactions entre machines, en plaçant les opérations importantes et les exécutions détaillées sur le serveur.

35. La réécriture est meilleure que les processeurs d’événements

1. Si un processeur d’événements lance une exception, les autres processeurs de la chaîne d’événements ne seront pas appelés, mais cela n’arrivera pas à la méthode virtuelle réécrite.
2. La réécriture est bien plus efficace que les processeurs d’événements associatifs, qui doivent itérer sur toute la liste de requêtes, ce qui prend plus de temps CPU.
3. Les événements peuvent être traités à l’exécution, avec plus de flexibilité, et plusieurs réponses peuvent être associées au même événement.
4. La règle courante est de traiter un événement dérivé, et la méthode de réécriture est meilleure.

36. Usage loyal. Diagnostic à l’exécution .NET

1. System.Diagnostics.Debug\Trace\EventLog fournit tous les outils nécessaires au programme pour ajouter des informations de diagnostic à l’exécution, et l’application peut écrire dans le journal d’événements système lorsque l’EventLog fournit l’ingrédient ;
2. Enfin, n’écrivez pas votre propre bibliothèque de diagnostic, .NET FCL possède déjà la bibliothèque de base dont nous avons besoin.

37. Utilisation des mécanismes de configuration standard

1、. La classe System.Windows.Application du framework .NET définit les propriétés pour établir un chemin de configuration commun ;
2. Application.LocalAppDataPath et Application.userDataPath généreront les noms de chemin du répertoire local des données et des données utilisateur ;
3. N’écrivez pas de données dans les fichiers de programme et les répertoires système Windows, ces emplacements nécessitent des permissions de sécurité plus élevées, ne vous attendez pas à ce que les utilisateurs aient des autorisations d’écriture.

38. Personnaliser et supporter la liaison de données

1. Les deux objets de BindingManager et CurrencyManager réalisent le transfert de données entre le contrôle et la source de données ;
2. Avantages de la liaison de données : utiliser la liaison de données est bien plus simple que d’écrire son propre code ; Il doit être utilisé pour des champs d’application autres que les éléments de données textuelles – d’autres propriétés d’affichage peuvent également être limitées ; Pour les liaisons de données WindowOS Forms, la capacité de gérer la synchronisation de multiples contrôles de sources de données liées aux vérifications ;
3. Lorsque l’objet ne prend pas en charge les attributs requis, vous pouvez supporter la liaison des données en bloquant l’objet courant puis en ajoutant l’objet désiré.

39. Utiliser. Validation .NET

1. Il y a cinq contrôles dans le ASP.NET pour vérifier la validité, et vous pouvez utiliser CustomValidator pour dériver une nouvelle classe afin d’ajouter votre propre authentificateur.
2. La validation de Windows nécessite un sous-système.Windows.Forms.Control.Validating pour écrire un gestionnaire d’événements.

40. Choisissez le ** approprié selon les besoins

1. Le tableau présente deux défauts évidents : il ne peut pas être redimensionné dynamiquement ; Le redimensionnement prend du temps ;
2. ArrayList mélange les caractéristiques des tableaux unidimensionnels et des listes chaînées, Queue et Stack sont des ensembles spéciaux basés sur Array ;
3. Lorsque le programme est plus flexible pour ajouter et supprimer des éléments, il peut créer des types plus robustes, et lorsqu’il crée une classe qui simule **, il doit implémenter des indexeurs et des interfaces IEnumberables pour celle-ci.

41. DataSet est meilleur que la structure personnalisée

1. Les ensembles de données présentent deux inconvénients : l’interaction entre les ensembles de données utilisant le mécanisme de sérialisation XML et le code non-.NET n’est pas très bonne ; DataSet est un conteneur très polyvalent ;
2. Les types puissants de DataSets enfreignent davantage de règles de conception, et leur efficacité de développement est bien supérieure à celle des conceptions plus élégantes écrites par eux-mêmes.

42. Utiliser des caractéristiques pour simplifier la réflexion

En concevant et en implémentant des classes de fonctionnalités qui obligent les développeurs à déclarer des types, méthodes et attributs dynamiquement utilisables, vous pouvez réduire les erreurs d’exécution des applications et améliorer la satisfaction des utilisateurs logiciels.

43. Évitez de surexploiter les réflexes

1. Les paramètres et valeurs de retour utilisés par les membres Invoke sont System.Object, qui convertit les types à l’exécution, mais la possibilité de problèmes est devenue plus probable.
2. L’interface nous permet d’obtenir un système plus clair et plus facile à maintenir, et la réflexion est un mécanisme de liaison tardive très puissant. .NET Framework l’utilise pour implémenter la liaison de données pour les contrôles Windows et web.

44. Créer des classes d’exception spécifiques pour l’application

1. La seule raison pour laquelle différentes classes d’exception sont nécessaires est de permettre aux utilisateurs d’adopter facilement différentes approches pour différentes erreurs lors de l’écriture de processeurs catch ;
2. Lorsqu’il peut y avoir différents comportements de réparation, nous devrions créer une variété de classes d’exception différentes, en fournissant tous les constructeurs supportés par la classe de base d’exception, nous pouvons créer une classe d’exception entièrement fonctionnelle pour l’application, et utiliser l’attribut InnerException pour sauvegarder toutes les informations d’erreur générées par des conditions d’erreur de niveau inférieur.

45. Donner la priorité aux garanties de sécurité anormales

1. La garantie forte d’exception offre le meilleur équilibre entre la récupération après l’exception et la gestion simplifiée des exceptions, et l’état du programme reste inchangé lorsque l’opération est interrompue en raison de l’exception.
2. Effectuer une copie défensive des données à modifier, modifier la copie défensive de ces données, l’opération centrale peut provoquer une exception, et la copie temporaire ainsi que l’objet original seront échangés ;
3. Les Terminators, méthodes Dispose() et méthodes cibles liées aux délégués doivent s’assurer qu’ils ne jettent aucune exception en aucune circonstance.

46. Minimiser l’interopérabilité

1. Il y a trois coûts liés à l’interopérabilité : le coût de l’énumération des données entre les tas gérés et non gérés, le coût de passage au code géré à non géré, et le travail de développement des développeurs travaillant avec des environnements hybrides ;
2. L’utilisation du type blittable en interop peut efficacement se répliquer entre environnements gérés et non gérés sans être affectée par la structure interne de l’objet.
3. Utiliser la fonction In/Out pour garantir les réplications multiples inutiles les plus appropriées, et améliorer les performances en déclarant comment les données sont énumérées.
4. Utiliser l’interopérabilité COM pour implémenter l’interopérabilité avec les composants COM de la manière la plus simple, utiliser P/Invoke pour appeler l’API Win32, ou utiliser le commutateur /CLR du compilateur C++ pour mélanger du code géré et non géré ;

47. Donner la priorité aux codes de sécurité

1. Éviter d’accéder à la mémoire non gérée autant que possible, et le stockage isolé ne peut empêcher l’accès du code géré et des utilisateurs de confiance.
2. Lorsque les assemblages s’exécutent sur le web, il faut envisager d’utiliser un stockage isolé, et lorsque certains algorithmes nécessitent des permissions de sécurité plus élevées, ces codes doivent être isolés dans un assembleur séparé.

48. Maîtriser les outils et ressources pertinents

1. Utiliser NUnit pour établir des tests unitaires automatiques (intégrés dans VS2010) ;
2. L’outil FXCop obtiendra le code IL dans l’assemblage, l’analysera par rapport aux règles de codage hétérogènes et aux meilleures pratiques, puis enfin signalera la violation.
3. ILDasm est un outil de démontage d’IL qui peut nous aider à mieux comprendre les détails ;
4. Shared Source CLI est un code source d’implémentation qui contient le noyau du framework .NET et le compilateur C#.




Précédent:Service Fabric - Concept de service avec états
Prochain:.net/c# SynchronizationContext pour les détails
Démenti:
Tous les logiciels, supports de programmation ou articles publiés par Code Farmer Network sont uniquement destinés à l’apprentissage et à la recherche ; Le contenu ci-dessus ne doit pas être utilisé à des fins commerciales ou illégales, sinon les utilisateurs assumeront toutes les conséquences. Les informations sur ce site proviennent d’Internet, et les litiges de droits d’auteur n’ont rien à voir avec ce site. Vous devez supprimer complètement le contenu ci-dessus de votre ordinateur dans les 24 heures suivant le téléchargement. Si vous aimez le programme, merci de soutenir un logiciel authentique, d’acheter l’immatriculation et d’obtenir de meilleurs services authentiques. En cas d’infraction, veuillez nous contacter par e-mail.

Mail To:help@itsvse.com