J'ai récemment programmé en C # et Java et je suis curieux de savoir où le meilleur endroit est d'initialiser mes champs de classe.
Dois-je le faire lors de la déclaration?:
public class Dice
{
private int topFace = 1;
private Random myRand = new Random();
public void Roll()
{
// ......
}
}
ou chez un constructeur?:
public class Dice
{
private int topFace;
private Random myRand;
public Dice()
{
topFace = 1;
myRand = new Random();
}
public void Roll()
{
// .....
}
}
Je suis vraiment curieux de savoir ce que certains d'entre vous, anciens combattants, pensez être la meilleure pratique. Je veux être cohérent et m'en tenir à une seule approche.
Réponses:
Mes règles:
null
,false
,0
,0.0
...).la source
default(T)
est toujours la valeur qui a une représentation binaire interne de0
.foreach
boucle avec "cela répète ce qui suit pour tous les éléments de la liste", nous n'avons pas besoin de reformuler constamment les valeurs par défaut de C #. Nous n'avons pas non plus besoin de prétendre que C # a une sémantique non initialisée. Étant donné que l'absence d'une valeur a une signification claire, il est bon de la laisser de côté. Si l'idéal est d'être explicite, vous devez toujours l'utilisernew
lors de la création de nouveaux délégués (comme cela était requis en C # 1). Mais qui fait ça? Le langage est conçu pour les codeurs consciencieux.En C #, cela n'a pas d'importance. Les deux exemples de code que vous donnez sont totalement équivalents. Dans le premier exemple, le compilateur C # (ou est-ce le CLR?) Va construire un constructeur vide et initialiser les variables comme si elles étaient dans le constructeur (il y a une légère nuance à cela que Jon Skeet explique dans les commentaires ci-dessous). S'il existe déjà un constructeur, toute initialisation "ci-dessus" sera déplacée en haut de celui-ci.
En termes de meilleures pratiques, le premier est moins sujet aux erreurs que le second, car quelqu'un pourrait facilement ajouter un autre constructeur et oublier de l'enchaîner.
la source
La sémantique de C # diffère légèrement de Java ici. En C #, l'affectation dans la déclaration est effectuée avant d'appeler le constructeur de la superclasse. En Java, cela se fait immédiatement après ce qui permet d'utiliser 'this' (particulièrement utile pour les classes internes anonymes), et signifie que la sémantique des deux formes correspond vraiment.
Si vous le pouvez, définissez les champs de manière définitive.
la source
Je pense qu'il y a une mise en garde. J'ai commis une fois une telle erreur: à l'intérieur d'une classe dérivée, j'ai essayé "d'initialiser à la déclaration" les champs hérités d'une classe de base abstraite. Le résultat a été qu'il existait deux ensembles de champs, l'un est "base" et l'autre est celui nouvellement déclaré, et il m'a fallu un certain temps pour déboguer.
La leçon: pour initialiser des champs hérités , vous le feriez à l'intérieur du constructeur.
la source
derivedObject.InheritedField
, se réfère-t-il à votre base ou dérivée?En supposant le type dans votre exemple, préférez certainement initialiser les champs dans le constructeur. Les cas exceptionnels sont:
Je pense toujours à la liste des champs en haut d'une classe comme la table des matières (ce qui est contenu ici, pas comment il est utilisé), et le constructeur comme l'introduction. Les méthodes sont bien sûr des chapitres.
la source
Et si je te le disais, ça dépend?
En général, j'initialise tout et je le fais de manière cohérente. Oui, c'est trop explicite, mais c'est aussi un peu plus facile à entretenir.
Si nous nous inquiétons des performances, eh bien, je n'initialise que ce qui doit être fait et je le place dans les zones où il en a le plus pour son argent.
Dans un système en temps réel, je me demande si j'ai même besoin de la variable ou de la constante.
Et en C ++, je ne fais souvent presque aucune initialisation à aucun endroit et je le déplace dans une fonction Init (). Pourquoi? Eh bien, en C ++ si vous initialisez quelque chose qui peut lever une exception lors de la construction d'un objet, vous vous ouvrez aux fuites de mémoire.
la source
Les situations sont nombreuses et diverses.
J'ai juste besoin d'une liste vide
La situation est claire. J'ai juste besoin de préparer ma liste et d'empêcher qu'une exception ne soit levée lorsque quelqu'un ajoute un élément à la liste.
Je connais les valeurs
Je sais exactement quelles valeurs je veux avoir par défaut ou je dois utiliser une autre logique.
ou
Liste vide avec valeurs possibles
Parfois, j'attends une liste vide par défaut avec la possibilité d'ajouter des valeurs via un autre constructeur.
la source
En Java, un initialiseur avec la déclaration signifie que le champ est toujours initialisé de la même manière, quel que soit le constructeur utilisé (si vous en avez plusieurs) ou les paramètres de vos constructeurs (s'ils ont des arguments), bien qu'un constructeur puisse par la suite changer la valeur (si elle n'est pas définitive). Ainsi, l'utilisation d'un initialiseur avec une déclaration suggère au lecteur que la valeur initialisée est la valeur que le champ a dans tous les cas , quel que soit le constructeur utilisé et quels que soient les paramètres transmis à n'importe quel constructeur. Par conséquent, utilisez un initialiseur avec la déclaration uniquement si et toujours si la valeur de tous les objets construits est la même.
la source
La conception de C # suggère que l'initialisation en ligne est préférée, ou ce ne serait pas dans le langage. Chaque fois que vous pouvez éviter une référence croisée entre différents endroits du code, vous êtes généralement mieux loti.
Il y a aussi la question de la cohérence avec l'initialisation de champ statique, qui doit être en ligne pour de meilleures performances. Les directives de conception de cadre pour la conception de constructeur disent ceci:
«Considérer» dans ce contexte signifie le faire à moins qu'il y ait une bonne raison de ne pas le faire. Dans le cas des champs d'initialisation statiques, une bonne raison serait que l'initialisation est trop complexe pour être codée en ligne.
la source
Être cohérent est important, mais c'est la question à se poser: "Ai-je un constructeur pour autre chose?"
En règle générale, je crée des modèles de transfert de données que la classe elle-même ne fait que travailler comme logement pour les variables.
Dans ces scénarios, je n'ai généralement pas de méthodes ni de constructeurs. Il serait ridicule pour moi de créer un constructeur dans le seul but d'initialiser mes listes, d'autant plus que je peux les initialiser en ligne avec la déclaration.
Donc, comme beaucoup d'autres l'ont dit, cela dépend de votre utilisation. Restez simple et ne faites rien de plus que vous n'avez pas à le faire.
la source
Considérez la situation où vous avez plus d'un constructeur. L'initialisation sera-t-elle différente pour les différents constructeurs? S'ils sont les mêmes, alors pourquoi répéter pour chaque constructeur? Ceci est conforme à l'instruction kokos, mais peut ne pas être lié aux paramètres. Supposons, par exemple, que vous souhaitiez conserver un indicateur qui indique comment l'objet a été créé. Ensuite, cet indicateur serait initialisé différemment pour différents constructeurs quels que soient les paramètres du constructeur. D'un autre côté, si vous répétez la même initialisation pour chaque constructeur, vous laissez la possibilité que vous modifiez (involontairement) le paramètre d'initialisation dans certains constructeurs mais pas dans d'autres. Ainsi, le concept de base ici est que le code commun devrait avoir un emplacement commun et ne pas être potentiellement répété à différents emplacements.
la source
Il y a un léger avantage en termes de performances à définir la valeur dans la déclaration. Si vous le définissez dans le constructeur, il est en réalité défini deux fois (d'abord à la valeur par défaut, puis réinitialisé dans le ctor).
la source
J'essaye normalement le constructeur pour ne rien faire mais obtenir les dépendances et initialiser les membres d'instance liés avec eux. Cela vous rendra la vie plus facile si vous voulez tester vos classes à l'unité.
Si la valeur que vous allez affecter à une variable d'instance n'est influencée par aucun des paramètres que vous allez transmettre à votre constructeur, affectez-la au moment de la déclaration.
la source
Pas une réponse directe à votre question sur la meilleure pratique mais un point de mise à jour important et connexe est que dans le cas d'une définition de classe générique, laissez-le sur le compilateur pour initialiser avec les valeurs par défaut ou nous devons utiliser une méthode spéciale pour initialiser les champs à leurs valeurs par défaut (si cela est absolument nécessaire pour la lisibilité du code).
Et la méthode spéciale pour initialiser un champ générique à sa valeur par défaut est la suivante:
la source
Lorsque vous n'avez pas besoin de logique ou de gestion des erreurs:
Lorsque vous avez besoin de logique ou de gestion des erreurs:
Sur https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html .
la source