public class MyClass
{
public object Prop1 { get; set; }
public object Prop2 { get; set; }
public object Prop3 { get; set; }
}
Supposons que j'ai un objet myObject
de MyClass
et que je doive réinitialiser ses propriétés, est-il préférable de créer un nouvel objet ou de réaffecter chaque propriété? Supposons que je n'ai aucune utilisation supplémentaire avec l'ancienne instance.
myObject = new MyClass();
ou
myObject.Prop1 = null;
myObject.Prop2 = null;
myObject.Prop3 = null;
Instancier un nouvel objet est toujours mieux, alors vous avez 1 endroit pour initialiser les propriétés (le constructeur) et pouvez facilement le mettre à jour.
Imaginez que vous ajoutez une nouvelle propriété à la classe, vous préférez mettre à jour le constructeur plutôt que d'ajouter une nouvelle méthode qui réinitialise également toutes les propriétés.
Maintenant, il y a des cas où vous voudrez peut-être réutiliser un objet, un cas où une propriété est très coûteuse à réinitialiser et que vous voudriez le conserver. Ce serait cependant plus spécialisé et vous auriez des méthodes spéciales pour réinitialiser toutes les autres propriétés. Vous voudriez toujours créer un nouvel objet parfois même pour cette situation.
Vous devriez certainement préférer créer un nouvel objet dans la grande majorité des cas. Problèmes de réaffectation de toutes les propriétés:
A
et la classe B
sont toutes deux des instances passées de la classe C
, alors elles doivent savoir si elles passeront jamais la même instance, et si oui, si l'autre l'utilise encore. Cela couple étroitement les classes qui, autrement, n'ont aucune raison d'être.Fraction
avec un numérateur et un dénominateur et que vous vouliez faire en sorte qu'elle soit toujours réduite (c'est-à-dire que le pgcd du numérateur et du dénominateur était 1). C'est impossible à faire si vous voulez permettre aux gens de définir le numérateur et le dénominateur publiquement, car ils peuvent passer par un état invalide pour passer d'un état valide à un autre. Par exemple. 1/2 (valide) -> 2/2 (invalide) -> 2/3 (valide).Ce sont tous des problèmes assez importants. Et ce que vous obtenez en échange du travail supplémentaire que vous créez n'est ... rien. La création d'instances d'objets est, en général, incroyablement bon marché, de sorte que les performances seront presque toujours totalement négligeables.
Comme l'autre réponse l'a mentionné, le seul moment où les performances peuvent être une préoccupation pertinente est si votre classe effectue des travaux de construction considérablement coûteux. Mais même dans ce cas, pour que cette technique fonctionne, vous devez être en mesure de séparer la partie coûteuse des propriétés que vous réinitialisez, de sorte que vous pourrez utiliser le modèle de poids de mouche ou similaire à la place.
En remarque, certains des problèmes ci-dessus pourraient être quelque peu atténués en n'utilisant pas de setters et en ayant à la place un public Reset
méthode sur votre classe qui prend les mêmes paramètres que le constructeur. Si, pour une raison quelconque, vous vouliez emprunter cette voie de réinitialisation, ce serait probablement une bien meilleure façon de le faire.
Pourtant, la complexité et la répétition supplémentaires qui s'ajoutent, ainsi que les points ci-dessus qu'il ne traite pas, sont toujours un argument très convaincant contre le faire, surtout lorsqu'il est comparé aux avantages inexistants.
Étant donné l'exemple très générique, c'est difficile à dire. Si "réinitialiser les propriétés" a un sens sémantique dans le cas du domaine, il sera plus logique pour le consommateur de votre classe d'appeler
MyObject.Reset(); // Sets all necessary properties to null
Que
MyObject = new MyClass();
Je n'exigerais JAMAIS de faire appel au consommateur de votre classe
MyObject.Prop1 = null;
MyObject.Prop2 = null; // and so on
Si la classe représente quelque chose qui peut être réinitialisé, elle doit exposer cette fonctionnalité via une méthode Reset()
, plutôt que de s'appuyer sur l'appel du constructeur ou de définir manuellement ses propriétés.
Comme le suggèrent Harrison Paine et Brandin, je réutiliserais le même objet et factoriserais l'initialisation des propriétés dans une méthode Reset:
public class MyClass
{
public MyClass() { this.Reset() }
public void Reset() {
this.Prop1 = whatever
this.Prop2 = you name it
this.Prop3 = oh yeah
}
public object Prop1 { get; set; }
public object Prop2 { get; set; }
public object Prop3 { get; set; }
}
Si le modèle d'utilisation prévu pour une classe est qu'un seul propriétaire conservera une référence à chaque instance, aucun autre code ne conservera de copie des références, et il sera très courant pour les propriétaires d'avoir des boucles qui doivent, plusieurs fois, "remplissez" une instance vide, utilisez-la temporairement et n'en aurez plus jamais besoin (une classe commune répondant à un tel critère serait StringBuilder
), alors cela peut être utile du point de vue des performances pour la classe pour inclure une méthode pour réinitialiser une instance à une condition comme neuve. Une telle optimisation ne vaut probablement pas grand-chose si l'alternative ne nécessite que la création de quelques centaines d'instances, mais le coût de création de millions ou de milliards d'instances d'objets peut s'additionner.
Sur une note connexe, il existe quelques modèles qui peuvent être utilisés pour les méthodes qui doivent renvoyer des données dans un objet:
La méthode crée un nouvel objet; renvoie la référence.
La méthode accepte une référence à un objet mutable et la remplit.
La méthode accepte une variable de type référence en tant que paramètre ref
et utilise l'objet existant si approprié, ou modifie la variable pour identifier un nouvel objet.
La première approche est souvent la plus simple sémantiquement. Le second est un peu gênant du côté appelant, mais peut offrir de meilleures performances si un appelant est souvent capable de créer un objet et de l'utiliser des milliers de fois. La troisième approche est un peu maladroite sur le plan sémantique, mais peut être utile si une méthode doit renvoyer des données dans un tableau et que l'appelant ne connaîtra pas la taille de tableau requise. Si le code appelant contient la seule référence au tableau, réécrire cette référence avec une référence à un tableau plus grand équivaudra sémantiquement à simplement agrandir le tableau (ce qui est le comportement souhaité sémantiquement). En utilisant List<T>
peut être plus agréable que d'utiliser un tableau redimensionné manuellement dans de nombreux cas, les tableaux de structures offrent une meilleure sémantique et de meilleures performances que les listes de structures.
Je pense que la plupart des gens qui souhaitent favoriser la création d'un nouvel objet n'ont pas de scénario critique: la collecte des ordures (GC). GC peut avoir un réel impact sur les performances dans les applications qui créent beaucoup d'objets (jeux de réflexion ou applications scientifiques).
Disons que j'ai un arbre d'expression qui représente une expression mathématique, où les nœuds internes sont des nœuds de fonction (ADD, SUB, MUL, DIV) et les nœuds de feuille sont des nœuds terminaux (X, e, PI). Si ma classe de nœuds a une méthode Evaluate (), elle fera probablement appel de manière récursive aux enfants et collectera des données via un nœud de données. À tout le moins, tous les nœuds terminaux devront créer un objet Data, puis ceux-ci pourront être réutilisés en remontant dans l'arbre jusqu'à ce que la valeur finale soit évaluée.
Supposons maintenant que j'ai des milliers de ces arbres et que je les ai évalués en boucle. Tous ces objets de données vont déclencher un GC et provoquer un impact sur les performances, un gros problème (jusqu'à 40% de perte d'utilisation du processeur pour certaines exécutions dans mon application - j'ai exécuté un profileur pour obtenir les données).
Une solution possible? Réutilisez ces objets de données et appelez simplement certains .Reset () sur eux après avoir fini de les utiliser. Les nœuds terminaux n'appelleront plus 'new Data ()', ils appelleront une méthode Factory qui gère le cycle de vie de l'objet.
Je le sais parce que j'ai une application qui a rencontré ce problème et qui l'a résolu.