Suite aux discussions ici sur SO, j'ai déjà lu plusieurs fois la remarque que les structures mutables sont «mauvaises» (comme dans la réponse à cette question ).
Quel est le problème réel avec la mutabilité et les structures en C #?
c#
struct
immutability
mutable
Dirk Vollmar
la source
la source
int
s mutables , lesbool
s et tous les autres types de valeurs sont mauvais. Il existe des cas de mutabilité et d'immuabilité. Ces cas dépendent du rôle que jouent les données, et non du type d'allocation / partage de mémoire.int
et nebool
sont pas mutables ...
-Syntax, rendant les opérations avec des données de type ref et des données de type valeur identiques, même si elles sont distinctement différentes. C'est une faute des propriétés de C #, pas des structures - certains langages offrent unea[V][X] = 3.14
syntaxe alternative pour muter sur place. En C #, vous feriez mieux d'offrir des méthodes de mutateur struct-membre comme 'MutateV (Action <ref Vector2> mutator)' et de l'utiliser commea.MutateV((v) => { v.X = 3; })
(l'exemple est trop simplifié en raison des limitations que C # a concernant leref
mot - clé, mais avec certains des solutions de contournement devraient être possibles) .x
), cette opération unique est de 4 instructions:ldloc.0
(charge la variable 0-index dans ...T
est le type. Ref est juste un mot clé qui fait passer une variable à une méthode elle-même, pas une copie de celle-ci. Il a également un sens pour les types de référence, car nous pouvons changer la variable , c'est-à-dire que la référence en dehors de la méthode pointera vers un autre objet après avoir été modifiée dans la méthode. Puisqu'ilref T
ne s'agit pas d'un type, mais de la façon de passer un paramètre de méthode, vous ne pouvez pas le placer<>
, car seuls les types peuvent y être placés. C'est donc tout simplement incorrect. Peut-être que ce serait pratique de le faire, peut-être que l'équipe C # pourrait le faire pour une nouvelle version, mais en ce moment ils travaillent sur certains ...Réponses:
Les structures sont des types de valeur, ce qui signifie qu'elles sont copiées lorsqu'elles sont transmises.
Donc, si vous modifiez une copie, vous ne modifiez que cette copie, pas l'original et pas d'autres copies qui pourraient être présentes.
Si votre structure est immuable, toutes les copies automatiques résultant du passage par valeur seront les mêmes.
Si vous voulez le changer, vous devez le faire consciemment en créant une nouvelle instance de la structure avec les données modifiées. (pas une copie)
la source
Par où commencer ;-p
Le blog d'Eric Lippert est toujours bon pour une citation:
Tout d'abord, vous avez tendance à perdre assez facilement les modifications ... par exemple, retirer des éléments d'une liste:
qu'est-ce que cela a changé? Rien d'utile ...
La même chose avec les propriétés:
vous obligeant à faire:
moins critique, il y a un problème de taille; les objets mutables ont tendance à avoir de multiples propriétés; pourtant, si vous avez une structure avec deux
int
s, astring
, aDateTime
et abool
, vous pouvez très rapidement graver beaucoup de mémoire. Avec une classe, plusieurs appelants peuvent partager une référence à la même instance (les références sont petites).la source
++
opérateur. Dans ce cas, le compilateur écrit simplement l'affectation explicite elle-même au lieu de bousculer le programmeur.Je ne dirais pas mal, mais la mutabilité est souvent un signe de surexcitation de la part du programmeur pour fournir un maximum de fonctionnalités. En réalité, cela n'est souvent pas nécessaire et cela, à son tour, rend l'interface plus petite, plus facile à utiliser et plus difficile à utiliser (= plus robuste).
Un exemple de ceci est les conflits de lecture / écriture et d'écriture / écriture dans les conditions de concurrence. Ceux-ci ne peuvent tout simplement pas se produire dans des structures immuables, car une écriture n'est pas une opération valide.
De plus, je prétends que la mutabilité n'est presque jamais réellement nécessaire , le programmeur pense simplement que cela pourrait être à l'avenir. Par exemple, il n'est tout simplement pas logique de modifier une date. Créez plutôt une nouvelle date basée sur l'ancienne. Il s'agit d'une opération bon marché, donc les performances ne sont pas prises en compte.
la source
float
composants d'une transformation graphique. Si une telle méthode renvoie une structure de champ exposé avec six composants, il est évident que la modification des champs de la structure ne modifiera pas l'objet graphique à partir duquel elle a été reçue. Si une telle méthode retourne un objet de classe mutable, peut-être que changer ses propriétés changera l'objet graphique sous-jacent et peut-être pas - personne ne sait vraiment.Les structures mutables ne sont pas mauvaises.
Ils sont absolument nécessaires dans des circonstances de haute performance. Par exemple, lorsque les lignes de cache et / ou la récupération de place deviennent un goulot d'étranglement.
Je n'appellerais pas l'utilisation d'une structure immuable dans ces cas d'utilisation parfaitement valables "le mal".
Je peux voir le point que la syntaxe de C # n'aide pas à distinguer l'accès d'un membre d'un type valeur ou d'un type référence, donc je suis tout à fait pour préférer les structures immuables, qui imposent l'immuabilité, aux structures mutables.
Cependant, au lieu de simplement étiqueter les structures immuables comme "mal", je conseillerais d'adopter le langage et de préconiser une règle empirique plus utile et constructive.
Par exemple: "les structures sont des types de valeurs, qui sont copiées par défaut. Vous avez besoin d'une référence si vous ne voulez pas les copier" ou "essayez de travailler avec des structures en lecture seule" .
la source
struct
avec des champs publics) que de définir une classe qui peut être utilisée, maladroitement, pour atteindre les mêmes fins, ou pour ajouter un tas d'ordure à une structure pour la faire émuler une telle classe (plutôt que de l'avoir se comporter comme un ensemble de variables collées ensemble avec du ruban adhésif, ce que l'on veut vraiment en premier lieu)Les structures avec des champs ou des propriétés publics mutables ne sont pas mauvaises.
Les méthodes Struct (distinctes des setters de propriétés) qui mutent "this" sont quelque peu mauvaises, uniquement parce que .net ne fournit pas un moyen de les distinguer des méthodes qui ne le font pas. Les méthodes de structure qui ne mutent pas "ceci" devraient être invocables même sur des structures en lecture seule sans avoir besoin de copie défensive. Les méthodes qui mutent "this" ne devraient pas du tout être invocables sur des structures en lecture seule. Étant donné que .net ne veut pas interdire aux méthodes de struct qui ne modifient pas "this" d'être invoquées sur des structures en lecture seule, mais ne veut pas autoriser la mutation des structures en lecture seule, il copie de manière défensive les structures dans read- seulement des contextes, obtenant sans doute le pire des deux mondes.
Malgré les problèmes de gestion des méthodes d'auto-mutation dans des contextes en lecture seule, cependant, les structures mutables offrent souvent une sémantique bien supérieure aux types de classes mutables. Considérez les trois signatures de méthode suivantes:
Pour chaque méthode, répondez aux questions suivantes:
Réponses:
La méthode 1 ne peut pas modifier foo et n'obtient jamais de référence. La méthode 2 obtient une référence de courte durée à foo, qu'elle peut utiliser pour modifier les champs de foo un certain nombre de fois, dans n'importe quel ordre, jusqu'à ce qu'elle revienne, mais elle ne peut pas conserver cette référence. Avant le retour de Method2, à moins qu'il n'utilise du code non sécurisé, toutes les copies qui auraient pu être faites de sa référence 'foo' auront disparu. La méthode 3, à la différence de la méthode 2, obtient une référence partageable de manière prometteuse à foo, et on ne sait pas ce qu'elle pourrait en faire. Il pourrait ne pas changer du tout foo, il pourrait changer foo puis revenir, ou il pourrait donner une référence à foo à un autre thread qui pourrait le muter d'une manière arbitraire à un moment futur arbitraire.
Les tableaux de structures offrent une merveilleuse sémantique. Étant donné RectArray [500] de type Rectangle, il est clair et évident de copier, par exemple, l'élément 123 vers l'élément 456, puis de définir ultérieurement la largeur de l'élément 123 sur 555, sans déranger l'élément 456. "RectArray [432] = RectArray [321 ]; ...; RectArray [123] .Width = 555; ". Savoir que Rectangle est une structure avec un champ entier appelé Width indiquera à tout le monde ce qu'il faut savoir sur les instructions ci-dessus.
Supposons maintenant que RectClass était une classe avec les mêmes champs que Rectangle et que l'on voulait faire les mêmes opérations sur un RectClassArray [500] de type RectClass. Peut-être que le tableau est censé contenir 500 références immuables pré-initialisées à des objets RectClass mutables. dans ce cas, le code approprié serait quelque chose comme "RectClassArray [321] .SetBounds (RectClassArray [456]); ...; RectClassArray [321] .X = 555;". Peut-être que le tableau est supposé contenir des instances qui ne vont pas changer, donc le code approprié ressemblerait plus à "RectClassArray [321] = RectClassArray [456]; ...; RectClassArray [321] = New RectClass (RectClassArray [321 ]); RectClassArray [321] .X = 555; " Pour savoir ce que l'on est censé faire, il faudrait en savoir beaucoup plus sur RectClass (par exemple, prend-il en charge un constructeur de copie, une méthode de copie depuis, etc. ) et l'utilisation prévue de la baie. Nulle part aussi propre que d'utiliser une structure.
Pour être sûr, il n'y a malheureusement aucun moyen intéressant pour une classe de conteneur autre qu'un tableau d'offrir la sémantique propre d'un tableau struct. Le mieux que l'on puisse faire, si l'on veut qu'une collection soit indexée avec par exemple une chaîne, serait probablement de proposer une méthode générique "ActOnItem" qui accepterait une chaîne pour l'index, un paramètre générique et un délégué qui serait passé par référence à la fois au paramètre générique et à l'élément de collection. Cela permettrait presque la même sémantique que les tableaux de structures, mais à moins que les personnes vb.net et C # ne puissent être poursuivies pour offrir une belle syntaxe, le code va être maladroit même s'il est raisonnablement performant (le passage d'un paramètre générique serait permettre l'utilisation d'un délégué statique et éviterait la nécessité de créer des instances de classe temporaires).
Personnellement, je suis irrité par la haine d'Eric Lippert et al. spew concernant les types de valeurs mutables. Ils offrent une sémantique beaucoup plus propre que les types de référence promiscueux qui sont utilisés partout. Malgré certaines limitations de la prise en charge par .net des types de valeur, il existe de nombreux cas où les types de valeur mutables sont mieux adaptés que tout autre type d'entité.
la source
Rectangle
je pourrais facilement trouver une position commune où vous obtenez un comportement très peu clair . Considérez que WinForms implémente unRectangle
type mutable utilisé dans laBounds
propriété du formulaire . Si je veux changer les limites, je voudrais utiliser votre belle syntaxe:form.Bounds.X = 10;
Cependant, cela ne change précisément rien sur le formulaire (et génère une belle erreur vous en informant). L'incohérence est le fléau de la programmation et c'est pourquoi l'immuabilité est souhaitée.Les types de valeurs représentent essentiellement des concepts immuables. Fx, cela n'a aucun sens d'avoir une valeur mathématique telle qu'un entier, un vecteur etc. et ensuite de pouvoir la modifier. Ce serait comme redéfinir le sens d'une valeur. Au lieu de modifier un type de valeur, il est plus logique d'attribuer une autre valeur unique. Pensez au fait que les types de valeurs sont comparés en comparant toutes les valeurs de ses propriétés. Le fait est que si les propriétés sont les mêmes, alors c'est la même représentation universelle de cette valeur.
Comme Konrad le mentionne, cela n'a pas de sens de changer une date non plus, car la valeur représente ce point unique dans le temps et non une instance d'un objet temporel qui a un état ou une dépendance au contexte.
J'espère que cela a un sens pour vous. Il s'agit bien plus du concept que vous essayez de capturer avec des types de valeur que des détails pratiques.
la source
int
itérateur, qui serait complètement inutile s'il était immuable. Je pense que vous confondez les «implémentations du compilateur / runtime des types de valeurs» avec des «variables typées en un type de valeur» - ce dernier est certainement modifiable en n'importe laquelle des valeurs possibles.Il y a quelques autres cas d'angle qui pourraient conduire à un comportement imprévisible du point de vue du programmeur.
Types de valeurs immuables et champs en lecture seule
Types et tableau de valeurs mutables
Supposons que nous ayons un tableau de notre
Mutable
structure et que nous appelions laIncrementI
méthode pour le premier élément de ce tableau. Quel comportement attendez-vous de cet appel? Doit-il changer la valeur du tableau ou seulement une copie?Ainsi, les structures mutables ne sont pas mauvaises tant que vous et le reste de l'équipe comprenez clairement ce que vous faites. Mais il y a trop de cas où le comportement du programme serait différent de celui attendu, ce qui pourrait conduire à des erreurs subtiles difficiles à produire et difficiles à comprendre.
la source
T[]
et un index entier, et en prévoyant qu'un accès à une propriété de typeArrayRef<T>
sera interprété comme un accès à l'élément de tableau approprié) [si une classe voulait exposer unArrayRef<T>
dans un autre but, elle pourrait fournir une méthode - par opposition à une propriété - pour le récupérer]. Malheureusement, de telles dispositions n'existent pas.Si vous avez déjà programmé dans un langage comme C / C ++, les structures peuvent être utilisées comme mutables. Il suffit de les passer avec ref, autour et il n'y a rien qui puisse mal tourner. Le seul problème que je trouve est les restrictions du compilateur C # et que, dans certains cas, je suis incapable de forcer la chose stupide à utiliser une référence à la structure, au lieu d'une copie (comme quand une structure fait partie d'une classe C # ).
Ainsi, les structures mutables ne sont pas mauvaises, C # les a rendues mauvaises. J'utilise des structures mutables en C ++ tout le temps et elles sont très pratiques et intuitives. En revanche, C # m'a fait abandonner complètement les structures en tant que membres de classes en raison de la façon dont elles gèrent les objets. Leur commodité nous a coûté la nôtre.
la source
readonly
, mais si l'on évite de faire ces choses, les champs de classe des types de structure sont très bien. La seule limitation vraiment fondamentale des structures est qu'un champ struct d'un type de classe mutable commeint[]
peut encapsuler l'identité ou un ensemble de valeurs immuable, mais ne peut pas être utilisé pour encapsuler des valeurs mutables sans encapsuler également une identité indésirable.Si vous vous en tenez à ce à quoi les structures sont destinées (en C #, Visual Basic 6, Pascal / Delphi, type de structure C ++ (ou classes) quand elles ne sont pas utilisées comme pointeurs), vous constaterez qu'une structure n'est pas plus qu'une variable composée . Cela signifie que vous les traiterez comme un ensemble complet de variables, sous un nom commun (une variable d'enregistrement à partir de laquelle vous référencez les membres).
Je sais que cela dérouterait beaucoup de gens profondément habitués à la POO, mais ce n'est pas une raison suffisante pour dire que de telles choses sont intrinsèquement mauvaises, si elles sont utilisées correctement. Certaines structures sont immuables comme elles l'entendent (c'est le cas de Python
namedtuple
), mais c'est un autre paradigme à considérer.Oui: les structures impliquent beaucoup de mémoire, mais ce ne sera pas précisément plus de mémoire en faisant:
par rapport à:
La consommation de mémoire sera au moins la même, voire plus dans le cas inmutable (bien que ce cas soit temporaire, pour la pile actuelle, selon la langue).
Mais, finalement, les structures sont des structures , pas des objets. Dans POO, la propriété principale d'un objet est son identité , qui la plupart du temps n'est pas plus que son adresse mémoire. Struct est synonyme de structure de données (pas un objet approprié, et ils n'ont donc pas d'identité de toute façon), et les données peuvent être modifiées. Dans d'autres langues, record (au lieu de struct , comme c'est le cas pour Pascal) est le mot et a le même but: juste une variable d'enregistrement de données, destinée à être lue à partir de fichiers, modifiée et sauvegardée dans des fichiers (c'est le principal utiliser et, dans de nombreuses langues, vous pouvez même définir l'alignement des données dans l'enregistrement, alors que ce n'est pas nécessairement le cas pour les objets correctement appelés).
Vous voulez un bon exemple? Les structures sont utilisées pour lire facilement les fichiers. Python possède cette bibliothèque car, étant donné qu'elle est orientée objet et ne prend pas en charge les structures, elle a dû l'implémenter d'une autre manière, ce qui est quelque peu moche. Les langages implémentant des structures ont cette fonctionnalité ... intégrée. Essayez de lire un en-tête bitmap avec une structure appropriée dans des langages comme Pascal ou C.Ce sera facile (si la structure est correctement construite et alignée; en Pascal, vous n'utiliseriez pas un accès basé sur des enregistrements mais des fonctions pour lire des données binaires arbitraires). Ainsi, pour les fichiers et l'accès direct (local) à la mémoire, les structures sont meilleures que les objets. Aujourd'hui, nous sommes habitués au JSON et au XML, et nous oublions donc l'utilisation de fichiers binaires (et comme effet secondaire, l'utilisation de structures). Mais oui: ils existent et ont un but.
Ils ne sont pas mauvais. Utilisez-les simplement dans le bon but.
Si vous pensez en termes de marteaux, vous voudrez traiter les vis comme des clous, trouver que les vis sont plus difficiles à plonger dans le mur, et ce sera la faute des vis, et ce seront les mauvaises.
la source
Imaginez que vous ayez un tableau de 1 000 000 de structures. Chaque struct représentant une équité avec des choses comme bid_price, offer_price (peut-être des décimales) et ainsi de suite, cela est créé par C # / VB.
Imaginez que le tableau est créé dans un bloc de mémoire alloué dans le tas non géré afin qu'un autre thread de code natif puisse accéder simultanément au tableau (peut-être du code à hautes performances faisant des calculs).
Imaginez que le code C # / VB écoute un flux du marché des changements de prix, ce code peut avoir à accéder à certains éléments du tableau (pour la sécurité quelle qu'elle soit), puis modifier certains champs de prix.
Imaginez que cela se fasse des dizaines, voire des centaines de milliers de fois par seconde.
Voyons les faits en face, dans ce cas, nous voulons vraiment que ces structures soient modifiables, elles doivent l'être parce qu'elles sont partagées par un autre code natif, donc la création de copies ne va pas aider; ils doivent l'être parce que faire une copie d'une structure de 120 octets à ces taux est une folie, surtout lorsqu'une mise à jour peut avoir un impact sur un ou deux octets.
Hugo
la source
Quand quelque chose peut être muté, il acquiert un sentiment d'identité.
Parce qu'il
Person
est mutable, il est plus naturel de penser à changer la position d' Eric que de cloner Eric, de déplacer le clone et de détruire l'original . Les deux opérations réussiraient à changer le contenu deeric.position
, mais l'une est plus intuitive que l'autre. De même, il est plus intuitif de passer Eric (comme référence) pour les méthodes pour le modifier. Donner à une méthode un clone d'Eric va presque toujours surprendre. Quiconque veut muterPerson
doit se rappeler de demander une référencePerson
ou il fera la mauvaise chose.Si vous rendez le type immuable, le problème disparaît; si je ne peux pas modifier
eric
, cela ne fait aucune différence pour moi si je reçoiseric
ou un clone deeric
. Plus généralement, un type est sûr à passer par valeur si tout son état observable est conservé dans des membres qui sont soit:Si ces conditions sont remplies, un type de valeur mutable se comporte comme un type de référence car une copie superficielle permettra toujours au récepteur de modifier les données d'origine.
L'intuitivité d'un immuable
Person
dépend cependant de ce que vous essayez de faire. S'ilPerson
ne représente qu'un ensemble de données sur une personne, il n'y a rien d'intuitif à ce sujet;Person
les variables représentent vraiment des valeurs abstraites , pas des objets. (Dans ce cas, il serait probablement plus approprié de le renommerPersonData
.) SiPerson
modélise en fait une personne elle - même, l'idée de créer en permanence et le déplacement des clones est stupide , même si vous avez évité l'écueil de penser que vous modifions l'original. Dans ce cas, il serait probablement plus naturel de simplement créerPerson
un type de référence (c'est-à-dire une classe).Certes, comme la programmation fonctionnelle nous a appris qu'il est avantageux de tout rendre immuable (personne ne peut secrètement conserver une référence à lui
eric
et le muter), mais comme ce n'est pas idiomatique dans la POO, cela ne sera toujours pas intuitif pour quiconque travaillant avec votre code.la source
foo
détient la seule référence à sa cible n'importe où dans l'univers, et que rien n'a capturé la valeur de hachage d'identité de cet objet, alors le champ de mutationfoo.X
équivaut sémantiquement à fairefoo
pointer vers un nouvel objet qui est exactement comme celui auquel il faisait précédemment référence, mais avecX
en maintenant la valeur souhaitée. Avec les types de classe, il est généralement difficile de savoir s'il existe plusieurs références à quelque chose, mais avec les structures, c'est facile: elles ne le font pas.Thing
un type de classe mutable, unThing[]
va encapsulent des identités d'objet - si l' on veut qu'elle veuille ou non - à moins que l' on peut faire en sorte que pasThing
dans le tableau auquel toutes les références extérieures existent sera jamais muter. Si l'on ne veut pas que les éléments du tableau encapsulent l'identité, il faut généralement s'assurer qu'aucun élément auquel il contient des références ne sera jamais muté, ou qu'aucune référence extérieure n'existera jamais aux éléments qu'il détient [les approches hybrides peuvent également fonctionner ]. Aucune des deux approches n'est terriblement pratique. SiThing
est une structure, aThing[]
encapsule uniquement les valeurs.Cela n'a rien à voir avec les structures (et pas avec C # non plus), mais en Java, vous pourriez avoir des problèmes avec des objets mutables lorsqu'ils sont par exemple des clés dans une carte de hachage. Si vous les modifiez après les avoir ajoutés à une carte et qu'elle modifie son code de hachage , de mauvaises choses peuvent se produire.
la source
Personnellement, quand je regarde le code, ce qui suit me semble assez maladroit:
data.value.set (data.value.get () + 1);
plutôt que simplement
data.value ++; ou data.value = data.value + 1;
L'encapsulation des données est utile lors du passage d'une classe et vous voulez vous assurer que la valeur est modifiée de manière contrôlée. Cependant, lorsque vous avez des fonctions de définition et d'obtention publiques qui ne font guère plus que de définir la valeur de ce qui est transmis, en quoi cela représente-t-il une amélioration par rapport au simple passage d'une structure de données publique?
Lorsque je crée une structure privée à l'intérieur d'une classe, je crée cette structure pour organiser un ensemble de variables en un groupe. Je veux pouvoir modifier cette structure dans la portée de la classe, pas obtenir des copies de cette structure et créer de nouvelles instances.
Pour moi, cela empêche une utilisation valide des structures utilisées pour organiser des variables publiques, si je voulais un contrôle d'accès, j'utiliserais une classe.
la source
Il y a plusieurs problèmes avec l'exemple de M. Eric Lippert. Il est conçu pour illustrer le fait que les structures sont copiées et comment cela pourrait être un problème si vous ne faites pas attention. En regardant l'exemple, je le vois comme le résultat d'une mauvaise habitude de programmation et pas vraiment un problème avec la structure ou la classe.
Une structure est supposée n'avoir que des membres publics et ne devrait pas nécessiter d'encapsulation. Si c'est le cas, ce devrait vraiment être un type / classe. Vous n'avez vraiment pas besoin de deux constructions pour dire la même chose.
Si vous avez une classe entourant une structure, vous appelleriez une méthode dans la classe pour muter la structure membre. C'est ce que je ferais comme une bonne habitude de programmation.
Une mise en œuvre appropriée serait la suivante.
Il semble que ce soit un problème d'habitude de programmation plutôt qu'un problème avec la structure elle-même. Les structures sont censées être mutables, c'est l'idée et l'intention.
Le résultat des changements voila se comporte comme prévu:
1 2 3 Appuyez sur n'importe quelle touche pour continuer. . .
la source
Les données mutables présentent de nombreux avantages et inconvénients. L'inconvénient d'un million de dollars est l'aliasing. Si la même valeur est utilisée à plusieurs endroits, et que l'un d'eux la change, alors elle semblera avoir changé comme par magie aux autres endroits qui l'utilisent. Ceci est lié, mais non identique, aux conditions de course.
L'avantage d'un million de dollars est parfois la modularité. L'état mutable peut vous permettre de masquer les informations changeantes du code qui n'a pas besoin de les connaître.
L'art de l'interprète aborde ces compromis en détail et donne quelques exemples.
la source
Je ne pense pas qu'ils soient mauvais s'ils sont utilisés correctement. Je ne le mettrais pas dans mon code de production, mais je le ferais pour quelque chose comme des simulations de tests unitaires structurés, où la durée de vie d'une structure est relativement petite.
En utilisant l'exemple d'Eric, vous pouvez peut-être créer une deuxième instance de cet Eric, mais faire des ajustements, car c'est la nature de votre test (c'est-à-dire la duplication, puis la modification). Peu importe ce qui se passe avec la première instance d'Eric si nous utilisons simplement Eric2 pour le reste du script de test, sauf si vous prévoyez de l'utiliser comme comparaison de test.
Cela serait surtout utile pour tester ou modifier le code hérité qui définit peu profond un objet particulier (le point des structures), mais en ayant une structure immuable, cela empêche son utilisation de manière ennuyeuse.
la source
Range<T>
type avec des membresMinimum
et desMaximum
champs de typeT
et de codeRange<double> myRange = foo.getRange();
, toutes les garanties sur le contenuMinimum
et leMaximum
contenu doivent provenirfoo.GetRange();
. Le fait d'Range
être une structure de champ exposé indiquerait clairement que cela n'ajoutera aucun comportement en soi.