Initialiser les champs de classe dans le constructeur ou la déclaration at?

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.

Devrais-je le faire à la déclaration?:

public class Dice
{
    private int topFace = 1;
    private Random myRand = new Random();

    public void Roll()
    {
       // ......
    }
}

Ou dans 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, pensent être la meilleure pratique. Je veux être cohérent et m'en tenir à une approche.

343
demandé sur Robert Columbia 2008-08-23 23:59:42

14 réponses

Mes règles:

  1. Ne pas initialiser les valeurs par défaut dans la déclaration (null, false, 0, 0.0...).
  2. préférez l'initialisation dans la déclaration si vous n'avez pas de paramètre constructeur qui modifie la valeur du champ.
  3. Si la valeur du champ change en raison d'un paramètre constructeur, placez l'initialisation dans les constructeurs.
  4. soyez cohérent dans votre pratique (la règle la plus importante).
251
répondu kokos 2015-12-16 09:28:15

En C# ça 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?) construira un constructeur vide et initialisera les variables comme si elles étaient dans le constructeur. S'il y a 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 chaîner il.

134
répondu Quibblesome 2008-08-24 16:04:02

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 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, rendez les champs définitifs.

14
répondu Tom Hawtin - tackline 2008-09-05 20:22:08

Je pense qu'il y a une mise en garde. J'ai déjà commis 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 était qu'il existait deux ensembles de champs, l'un est "base" et l'autre est les nouveaux déclarés, et cela m'a coûté un certain temps pour déboguer.

La leçon: pour initialiser les champs hérités de , vous le feriez à l'intérieur du constructeur.

12
répondu xji 2016-05-11 09:23:17

En supposant le type dans votre exemple, préférez définitivement initialiser les champs dans le constructeur. Les cas exceptionnels sont:

  • champs dans les classes/méthodes statiques
  • champs tapés comme static / final / et al

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.

5
répondu Noel 2008-08-23 21:56:29

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 à maintenir.

Si nous sommes préoccupés par la performance, Eh bien, je n'initialise que ce qui doit être fait et le place dans les zones où il donne le plus pour l'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 fais souvent à côté de pas d'initialisation dans placez-le et déplacez-le dans une fonction Init (). Pourquoi? Eh bien, en C++ si vous initialisez quelque chose qui peut lancer une exception pendant la construction de l'objet, vous vous ouvrez aux fuites de mémoire.

3
répondu Dan Blair 2012-04-10 19:46:15

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 plus d'un) ou les paramètres de vos constructeurs (s'ils ont des arguments), bien qu'un constructeur puisse ensuite changer la valeur (s'il n'est pas final). Ainsi, l'utilisation d'un initialiseur avec une déclaration suggère à un lecteur que la valeur initialisée est la valeur que le champ a dans tous les cas , quel que soit le constructeur est utilisé et indépendamment des paramètres passés à n'importe quel constructeur. Par conséquent, utilisez un initialiseur avec la déclaration uniquement si, et toujours si, la valeur pour tous les objets construits est la même.

2
répondu Raedwald 2016-01-17 20:13:38

Les situations sont nombreuses et variées.

J'ai juste besoin d'une liste vide

La situation est claire. J'ai juste besoin de préparer ma liste et de prévenir une exception levée lorsque quelqu'un ajoute un élément à la liste.

public class CsvFile
{
    private List<CsvRow> lines = new List<CsvRow>();

    public CsvFile()
    {
    }
}

Je connais les valeurs

Je sais exactement quelles valeurs je veux avoir par défaut ou j'ai besoin d'utiliser une autre logique.

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = new List<string>() {"usernameA", "usernameB"};
    }
}

Ou

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = GetDefaultUsers(2);
    }
}

Liste vide avec les valeurs possibles

Parfois, je m'attends une liste vide par défaut avec la possibilité d'ajouter des valeurs via un autre constructeur.

public class AdminTeam
{
    private List<string> usernames = new List<string>();

    public AdminTeam()
    {
    }

    public AdminTeam(List<string> admins)
    {
         admins.ForEach(x => usernames.Add(x));
    }
}
2
répondu Miroslav Holec 2016-09-18 18:42:07

Il y a un léger avantage de performance à définir la valeur dans la déclaration. Si vous le définissez dans le constructeur, il est en fait défini deux fois (d'abord à la valeur par défaut, puis réinitialisé dans le ctor).

1
répondu John Meagher 2008-08-29 15:47:24

La conception de C# suggère que l'initialisation en ligne est préférée, sinon elle ne serait pas dans le langage. Chaque fois que vous pouvez éviter une référence croisée entre différents endroits dans le code, vous êtes généralement mieux lotis.

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 du Cadre pour Constructor Design disent ceci:

✓ envisagez plutôt d'initialiser les champs statiques en ligne que d'utiliser explicitement des constructeurs statiques, car le runtime est capable d'optimiser les performances des types qui n'ont pas de constructeur statique explicitement défini.

" considérer " dans ce contexte signifie le faire à moins qu'il n'y ait une bonne raison de ne pas le faire. Dans le cas des champs d'initialisation statiques, une bonne raison serait si l'initialisation est trop complexe pour être codée en ligne.

1
répondu Edward Brey 2017-05-14 05:46:59

Être cohérent est important, mais c'est la question à se poser: "Ai-je un constructeur pour autre chose?"

Typiquement, je crée des modèles pour les transferts de données que la classe elle-même ne fait rien sauf travailler comme logement pour les variables.

Dans ces scénarios, Je n'ai généralement pas de méthodes ou de constructeurs. Il me semblerait stupide de créer un constructeur dans le but exclusif d'initialiser Mes listes, d'autant plus que je peux les initialiser en ligne avec le déclaration.

Ainsi que beaucoup d'autres l'ont dit, cela dépend de votre utilisation. Gardez les choses simples, et ne faites rien de plus que vous n'avez pas à.

1
répondu MeanJerry 2017-05-24 20:27:19

Considérez la situation où vous avez plus d'un constructeur. Sera l'initialisation être différentes pour les différents constructeurs? Si elles seront 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. Disons, par exemple, vous voulez garder un indicateur qui montre comment l'objet a été créé. Ensuite, cet indicateur serait initialisé différemment pour différents constructeurs indépendamment des paramètres du constructeur. Sur l'autre hand, si vous répétez la même initialisation pour chaque constructeur, vous laissez la possibilité de changer (involontairement) le paramètre d'initialisation dans certains constructeurs mais pas dans d'autres. Donc, le concept de base ici est que le code commun devrait avoir un emplacement commun et ne pas être potentiellement répété dans des endroits différents. Je dirais donc toujours le mettre dans la déclaration jusqu'à ce que vous ayez une situation spécifique où cela ne fonctionne plus pour vous.

1
répondu Joe Programmer 2017-10-20 21:05:40

J'essaie normalement le constructeur de ne rien faire d'autre que d'obtenir les dépendances et d'initialiser les membres d'instance associés avec eux. Cela vous rendra la vie plus facile si vous voulez tester vos classes.

Si la valeur que vous allez attribuer à une variable d'instance n'est influencée par aucun des paramètres que vous allez passer à votre constructeur, affectez-la au moment de la déclaration.

0
répondu Iker Jimenez 2008-08-24 15:59:11

Pas une réponse directe à votre question sur la meilleure pratique mais un point de rafraîchissement important et connexe est que dans le cas d'une définition de classe générique, laissez-la sur le compilateur pour initialiser avec des 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

class MyGeneric<T>
{
    T data;
    //T data = ""; // <-- ERROR
    //T data = 0; // <-- ERROR
    //T data = null; // <-- ERROR        

    public MyGeneric()
    {
        // All of the above errors would be errors here in constructor as well
    }
}

Et la méthode spéciale pour initialiser un champ générique à sa valeur par défaut est la suivante:

class MyGeneric<T>
{
    T data = default(T);

    public MyGeneric()
    {           
        // The same method can be used here in constructor
    }
}
0
répondu user1451111 2018-07-08 12:32:06