C'est une très bonne question, car j'ai vu un certain nombre de développeurs C ++ écrire un terrible code C #.
Il vaut mieux ne pas penser à C # comme "C ++ avec une syntaxe plus agréable". Ce sont des langues différentes qui nécessitent des approches différentes dans votre réflexion. C ++ vous oblige à penser constamment à ce que le CPU et la mémoire feront. C # n'est pas comme ça. C # est spécifiquement conçu pour que vous ne pensiez pas au processeur et à la mémoire, mais que vous pensiez plutôt au domaine commercial pour lequel vous écrivez .
Un exemple de cela que j'ai vu est que beaucoup de développeurs C ++ aimeront utiliser pour les boucles car ils sont plus rapides que foreach. C'est généralement une mauvaise idée en C # car cela restreint les types possibles de la collection sur laquelle itérer (et donc la réutilisation et la flexibilité du code).
Je pense que la meilleure façon de réajuster du C ++ au C # est d'essayer d'aborder le codage sous un angle différent. Au début, cela sera difficile, car au fil des ans, vous serez complètement habitué à utiliser le fil "que font le CPU et la mémoire" dans votre cerveau pour filtrer le code que vous écrivez. Mais en C #, vous devriez plutôt penser aux relations entre les objets dans le domaine métier. "Qu'est-ce que je veux faire" par opposition à "que fait l'ordinateur".
Si vous voulez faire quelque chose pour tout dans une liste d'objets, au lieu d'écrire une boucle for sur la liste, créez une méthode qui prend an IEnumerable<MyObject>
et utilisez une foreach
boucle.
Vos exemples spécifiques:
Contrôle de la durée de vie des ressources qui nécessitent un nettoyage déterministe (comme les fichiers). C'est facile à avoir en main mais comment l'utiliser correctement lorsque la propriété de la ressource est transférée [... entre les threads]? En C ++, j'utiliserais simplement des pointeurs partagés et le laisserais prendre en charge la «collecte des ordures» juste au bon moment.
Vous ne devriez jamais faire cela en C # (en particulier entre les threads). Si vous devez faire quelque chose dans un fichier, faites-le au même endroit à la fois. Il est correct (et même une bonne pratique) d'écrire une classe wrapper qui gère la ressource non managée qui est transmise entre vos classes, mais n'essayez pas de passer un fichier entre les threads et demandez à des classes distinctes d'écrire / lire / fermer / ouvrir. Ne partagez pas la propriété des ressources non gérées. Utilisez le Dispose
modèle pour gérer le nettoyage.
Lutte constante avec des fonctions prioritaires pour des génériques spécifiques (j'aime des choses comme la spécialisation partielle de modèles en C ++). Dois-je simplement abandonner toute tentative de faire une programmation générique en C #? Peut-être que les génériques sont limités à dessein et que ce n'est pas C -ish pour les utiliser sauf pour un domaine spécifique de problèmes?
Les génériques en C # sont conçus pour être génériques. Les spécialisations des génériques devraient être gérées par des classes dérivées. Pourquoi un List<T>
comportement différent devrait-il se produireList<int>
ou d'un List<string>
? Toutes les opérations sur List<T>
sont génériques pour s'appliquer à tout List<T>
. Si vous souhaitez modifier le comportement de la .Add
méthode sur a List<string>
, créez une classe dérivée MySpecializedStringCollection : List<string>
ou une classe composée MySpecializedStringCollection : IList<string>
qui utilise le générique en interne mais fait les choses de manière différente. Cela vous aide à éviter de violer le principe de substitution de Liskov et de visser royalement les autres qui utilisent votre classe.
Fonctionnalité de type macro. Bien qu'il s'agisse généralement d'une mauvaise idée, pour certains domaines de problèmes, il n'y a pas d'autre solution de contournement (par exemple, l'évaluation conditionnelle d'une instruction, comme avec les journaux qui ne devraient aller qu'aux versions de débogage). Ne pas les avoir signifie que je dois en mettre plus si (condition) {...} passe-partout et ce n'est toujours pas égal en termes de déclenchement d'effets secondaires.
Comme d'autres l'ont dit, vous pouvez utiliser des commandes de préprocesseur pour ce faire. Les attributs sont encore meilleurs. En général, les attributs sont le meilleur moyen de gérer des choses qui ne sont pas des fonctionnalités "essentielles" pour une classe.
En bref, lors de l'écriture de C #, gardez à l'esprit que vous devez penser entièrement au domaine de l'entreprise et non au processeur et à la mémoire. Vous pouvez optimiser plus tard si nécessaire , mais votre code doit refléter les relations entre les principes commerciaux que vous essayez de mapper.
C#
pour générer un autre code. Vous pouvez lire unCSV
ou unXML
ou ce que vous avez en entrée et générer unC#
ou unSQL
fichier. Cela peut être plus puissant que l'utilisation de macros fonctionnelles.