2010-05-10 4 views
11

Possible en double:
Initialize class fields in constructor or at declaration?Est-il une mauvaise pratique à l'extérieur des champs d'initialiser un constructeur explicite

Nous plaidons sur les pratiques de codage. Les exemples ici sont un peu trop simples, mais la vraie affaire a plusieurs constructeurs. Afin d'initialiser les valeurs simples (par exemple les dates à leur valeur min) j'ai déplacé le code hors des constructeurs et dans les définitions de champ.

public class ConstructorExample 
{ 
    string _string = "John"; 
} 

public class ConstructorExample2 
{ 
    string _string; 

    public ConstructorExample2() 
    { 
     _string = "John"; 
    } 
} 

Comment doit-il être fait par le livre? J'ai tendance à être très au cas par cas et donc je suis peut-être un peu laxiste à propos de ce genre de chose. Cependant, je pense que le rasoir occams me dit de déplacer l'initialisation de plusieurs constructeurs. Bien sûr, je pourrais toujours déplacer cette initialisation partagée dans une méthode privée.

La question est essentiellement ... est l'initialisation des champs où ils sont définis par opposition au constructeur mauvais en aucune façon?

L'argument que je suis confronté est une gestion des erreurs, mais je ne pense pas qu'il est pertinent car il n'y a aucune exception possible qui ne seront pas ramassées au moment de la compilation.

+0

duplication possible de [Meilleure pratique: Initialiser les champs de classe dans le constructeur ou à la déclaration?] (Http://stackoverflow.com/questions/24551/best-practice-initialize-class-fields-in-constructor-or-at -déclaration) –

+1

c'est mais je ne trouve pas les réponses dans ce fil pour être convaincant - le premier est un peu dogmatique. La référence à FXCop est un argument fort, tout comme l'argument de maintenance et de chaînage des constructeurs mais à part cela, rien de trop factuel. –

+3

Pour quelques conséquences intéressantes des différences subtiles entre l'initialisation sur le terrain et dans le corps, voir http://blogs.msdn.com/ericlippert/archive/2008/02/15/why-do-initializers-run-in- le-contraire-ordre-comme-constructeurs-part-one.aspx et http://blogs.msdn.com/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite- order-as-constructors-part-two.aspx –

Répondre

13

Ce n'est pas nécessairement mauvais pour initialiser des valeurs en dehors du constructeur, et le problème que vous avez ici:

string _string; 

    public ConstructorExample2() 
    { 
     _string = "John"; 
    } 

Est-ce que si vous avez plusieurs constructeurs vous devez vous rappeler soit
1. Réinitialiser _string dans chaque constructeur
2. Séparer la logique à une méthode en commun et appeler cette méthode dans tous les constructeurs
3. appelez le constructeur avec la logique en elle, des autres constructeurs. (Chaîne les constructeurs)
Maintenant, ce n'est pas nécessairement un problème, mais vous devez vous rappeler de le faire. En l'initialisant en dehors du constructeur, c'est fait pour vous. C'est une chose de moins que vous devez vous rappeler de faire.

+7

Vous n'avez pas besoin de réinitialiser la chaîne _ dans chaque constructeur. Au lieu de cela, vous pouvez chaîner des cteurs en utilisant un 'public MyClass(): this (String.Vide) ' – Oliver

+0

Avec le commentaire, j'aime bien cela comme une réponse équilibrée. –

+0

Oliver se soucie de fournir un certain raisonnement? –

0

Je préfère initialiser des champs simples comme ceux en dehors du constructeur.

Il ne devrait pas poser de problèmes puisque la compilation se déplace effectivement ces initialisations dans le constructeur à la compilation de toute façon.

2

Par défaut, Microsoft FxCop recommande d'initialiser les champs en utilisant le constructeur. This question est également une copie de celui-ci et devrait fournir un aperçu.

Avec les classes statiques, vous devrez noter quelques subtilités à l'adresse indiquée à this question.

+0

Pourquoi FxCop fait-il cette recommandation? –

+0

@Robert: content que vous ayez demandé. J'ai mis à jour ma réponse pour inclure des documents à l'appui sous la forme d'autres questions/réponses. Mon point de vue est que les initialiseurs de champ s'exécutent avant les constructeurs et donnent aux constructeurs un objet "plus prêt" avec lequel travailler. –

+2

Mon erreur - semble FxCop recommande que ce dans le cas des classes statiques: http://msdn.microsoft.com/en-us/library/ms182275.aspx –

0

Si l'initialisation de la variable sera le même, peu importe ce que les arguments sont transmis au constructeur, il n'a pas de sens encombrer la méthode constructeur avec le code d'initialisation inutile. Dans ce cas, j'initialise sur place.

0

Inialer les champs dans le constructeur est meilleur. De cette façon, si/quand un constructeur différent est ajouté, vous savez que tous les champs commencent avec des valeurs nulles/par défaut et vous pouvez les initialiser de manière appropriée.

1

Dans l'exemple ci-dessus l'attribution de « John » à _string n'a pas de dépendance logique sur toutes les variables et, par conséquent, il devrait être en dehors du constructeur dans le initialiseur sur le terrain.

Tant que ce n'est pas possible d'initialiser l'objet dans un état non utilisable alors il n'a pas d'importance.

Lorsque le code est compilé les deux approches seront les mêmes de toute façon.

15

Notez que toutes ces initialisation de niveau de déclaration terrain sera effectuée une fois pour chaque constructeur de la chaîne, même si le constructeur lui-même définit le champ à autre chose.

Si vous enchaînez les constructeurs ensemble, les champs seront initialisés dans la commune, tout d'abord, le constructeur qui est appelé.

Regardez cet exemple:

using System; 

namespace ClassLibrary3 
{ 
    public class Class1 
    { 
     private string _Name = "Lasse"; 

     public Class1() 
     { 
     } 

     public Class1(int i) 
      : this() 
     { 
     } 

     public Class1(bool b) 
     { 
      _Name = "Test"; 
     } 
    } 
} 

Ce code compile comme ceci:

using System; 

namespace ClassLibrary3 
{ 
    public class Class1 
    { 
     private string _Name; 

     public Class1() 
     { 
      _Name = "Lasse" 
     } 

     public Class1(int i) 
      : this() 
     { 
      // not here, as this() takes care of it 
     } 

     public Class1(bool b) 
     { 
      _Name = "Lasse" 
      _Name = "Test"; 
     } 
    } 
} 
+1

Je regrette de ne pas pouvoir vous avoir plus d'une fois. ;-) – Oliver

+0

Ouais, désolé je ne pouvais pas marquer cela comme la réponse. Le point sur le nom des initialisers qui sont appelés dans un premier ordre comme étant contre-intuitif est assez fort - surtout dans les scénarios d'héritage et dans la connaissance de ce qui a été ou n'a pas été initialisé. –

1

ne suis pas sûr C#, mais dans le code source Java, ils semblent préférer le constructeur, par exemple:

public class String{ 
    char[] value; 
    int offset; 
    ... 
    public String(){ 
     value = new char[0]; 
     offset = 0; 
     ... 
    } 
} 
+0

Je discute avec programmeur ac/Java;) Il y a un moyen d'initialiser comme ceci dans Java http://java.sun.com/docs/books/tutorial/java/javaOO/initial.html –

+0

Je sais qu'il y a, mais cet extrait provient du code source Java lui-même. Habituellement, quand je soupçonne quelque chose, je vérifie comment les grands esprits qui ont écrit Java l'ont résolu. – medopal

1

Je pense que pour les initialisations simples comme qu'il est bien de le faire dans la déclaration. Cependant, je ne comprends pas l'argument de gestion des erreurs. Même s'il y a une exception dans l'initialisation, je pense que vous constaterez que votre mécanisme normal de gestion des erreurs fonctionnera de la même manière. Il lèvera toujours une exception lorsque vous appelez le constructeur.

+0

J'ai tendance à être d'accord, mais je ne veux pas le rejeter d'emblée. –

1

J'ai tendance à initialiser des choses dans l'accesseur get, où elles sont utilisées pour la première fois. Si null alors initialiser et tout ça.

+0

Je n'avais pas considéré cette option. Le seul problème est dans le cas où une valeur nulle est une valeur valide, mais n'est pas la valeur par défaut. –

+0

Vous pouvez toujours faire un booléen pour indiquer l'état initialisé au lieu de s'appuyer sur "if (value == null) {initialize;}". J'aime l'initialisation à l'accès - je pense que c'est plus souvent appelé "initialisation paresseuse". C'est très pratique lorsque vous travaillez avec une base de données. Cela facilite également la mise en cache des choses si vous le souhaitez. – quillbreaker

+0

Je ne pense pas que je prendrais cette approche pour un initialiseur de valeur littérale (comme une chaîne littérale). Il échange une affectation unique avec un test de null à chaque acquisition et rend le code plus complexe. Pour d'autres scenerios, en particulier avec des initialisations d'objets grandes ou lentes, cela peut avoir du sens. –