Quel est le moyen approprié de mettre en œuvre une affectation par valeur pour un type de référence? Je veux effectuer une mission, mais ne pas changer la référence.
Voici ce dont je parle: p> existe une sorte de convention que je devrais utiliser pour ça? Je me sens comme si je ne suis pas la première personne qui a rencontré ceci. Merci. P> EDIT: P> WOW. Je pense que je dois adapter ma question plus avant le problème réel que je suis confronté. Je reçois beaucoup de réponses qui ne répondent pas à la nécessité de ne pas changer la référence. Le clonage n'est pas le problème ici. Le problème réside dans l'attribution du clone. P> J'ai de nombreuses classes qui dépendent de A - ils partagent tous une référence au même objet de la classe A. Donc, chaque fois que l'on change A, il est reflété dans les autres. , droit? C'est tout bien et bien jusqu'à ce que l'une des classes essaie de le faire: P> myA = new A();
12 Réponses :
Je pense que vous devriez utiliser une structure au lieu d'une classe que, comme les structures fonctionnent par valeur et non par référence. P>
Cette classe contient des fonctions afin qu'il ne soit pas aussi facile de le changer à une structure, mais je garderai cela à l'esprit.
@James - une structure peut contenir des méthodes tout aussi facilement qu'une classe.
Woah là-bas ... Les structures sont très rarement le bon choix et devraient presque toujours être immuables ...
Woops. Eu mon esprit en mode C. Quoi qu'il en soit, les structures en C # sont des types de valeur et donc une seule instance de la struct n'a pas pu être référencée par plusieurs objets.
@Andreway: Notez aussi facilement. Le seul moyen de restreindre une méthode de structure à utiliser sur des emplacements de stockage mutable consiste à utiliser le dossier de dossier de base statique de manière plutôt peu importante (Ref it); ... mystruct.Dosomething (Ref Soméexpression); code>. Si une méthode ne doit être utilisée que sur des emplacements de stockage mutable est écrite à l'aide de la syntaxe de méthode d'instance, le compilateur ne sera pas squawk s'il est utilisé sur un emplacement en lecture seule ou une valeur temporaire, mais générera de manière silencieuse.
Si l'objet était immuable, toutes les instances de classes qui dépendent de cet objet immuable ne seraient pas en mesure de manipuler l'objet immuable sans perdre leur référence.
Si la fonction withValue crée une «nouvelle» instance, c'est une référence «nouvelle» qui ne relierait pas aux références existantes.
En effet - ce qui serait le point. Tout pourrait être convaincu que ce qu'il ne changerait pas. C'est beaucoup la manière fonctionnelle de regarder la programmation.
Eh bien, ce que j'ai doit changer, puis tout le monde devrait être informé de la modification. En le mettant de cette façon, on dirait que je devrais utiliser le modèle d'observateur, que je pense est lourde par rapport aux alternatives. Quelqu'un a suggéré le modèle d'observateur ci-dessous et je leur ai donné +1 mais je pense que cela serait trop excité.
Une approche consiste à utiliser un constructeur de copie. E.g., P>
myclass origine = ...;; MyClass Copy = Nouveau MyClass (Orig); P>
où vous copiez les éléments de MyClass. Selon le nombre de types de référence, la classe contient cela pourrait impliquer une utilisation récursive des constructeurs de copies. P>
Désolé, cette réponse ne s'applique pas au problème. L'obtention d'une copie n'est pas le problème à la main.
Avez-vous considéré le modèle observateur / observable? Au lieu de plusieurs classes partageant une référence à une référence à ceux qui pourraient plutôt gérer les événements d'un (par exemple, surpropertychanged). Lorsque vous désérialisiez A, vous pouvez copier sa liste d'invocation pour maintenir la connexion ...
Je connais le modèle d'observateur et je pense que cela fonctionnerait, mais je ne préférerais pas l'utiliser car il est assez encombrant par rapport au problème que je m'adresse. Je vous donne +1 mais je ne marquons pas cela comme la meilleure réponse.
D'autres ont suggéré de cloner leur réponse, mais ce n'est qu'une partie de l'accord. Vous souhaitez également utiliser les résultats d'un clone (éventuellement profond) pour remplacer le contenu d'un objet existant. C'est une exigence très c ++.
Cela ne vient pas très souvent dans C #, il n'y a donc pas de nom de méthode standard ou de signification de l'opérateur "Remplacer le contenu de cet objet avec une copie du contenu de cet objet". P>
La raison pour laquelle il se produit si souvent en C ++ est dû à la nécessité de suivre la propriété de sorte que le nettoyage puisse être effectué. Si vous avez un membre: p> Vous avez l'avantage qu'il sera détruit correctement lorsque l'objet enfermé est détruit. Mais si vous voulez le remplacer par un nouveau vecteur, vous avez besoin de Vous pouvez désormais échanger dans une nouvelle, mais vous devez vous rappeler de supprimer l'ancien d'abord et dans le destructeur de la classe entourant. en C #, vous n'avez pas besoin de vous inquiéter de cela. Il y a une façon de droite: p> Vous n'avez pas à le nettoyer et que vous pouvez échanger dans une nouvelle par référence. Meilleur des deux. P> Si vous avez plusieurs objets "client" qui doivent contenir une référence à un objet et que vous voulez pouvoir pouvoir Pour remplacer cet objet, vous leur feriez une référence à un objet intermédiaire qui agirait comme un "wrapper". p> Les autres classes tiendraient une référence à la remplaçable Swap code> pour faire cela efficace. Sinon, vous pourriez avoir: p> Replaceable<FileStream> _fileStream;
Mais la chose est, je ne peut pas i> changer la référence. Cela brise la cravate aux autres classes qui ont cette vieille référence.
HMM .. Un objet intermédiaire et un opérateur de conversion implicite sur la surcharge essentiellement de l'opérateur d'affectation afin que la conversion de T à remplaçable
Cache = NULL n'affecterait qu'un client et pourrait être corrigé en faisant du cache en lecture seule ou en lui donnant un setter privé. cache.instance = NULL affecterait tous les utilisateurs du même cache, mais vous pouvez faire une instance avoir un setter qui jette si la nouvelle valeur est null, ce qui rend cette erreur très facile à piéger.
J'ai parlé avec quelques utilisateurs potentiels de mon API et ils conviennent que travailler avec des objets intermédiaires n'est pas très intuitif / souhaitable, surtout compte tenu des alternatives.
Être intéressé à savoir quelles alternatives vous envisagez
Pour ce que vous voulez faire, je pense qu'A.ValueeAsign (Autresa) est la meilleure façon. p>
Étant donné que vous souhaitez avoir une référence d'un autour, garantissant que la référence n'est pas détruite est la clé. p>
Ne seriez-vous pas également servi en utilisant un modèle singleton ici aussi? P>
C'est drôle que tu mentionnes ça. Je viens de faire le refactoriser pour ne pas être un singleton afin qu'il soit plus compatible avec TDD. Dans mon cas, A est un cache qui doit être partagé par plusieurs classes. J'ai lu quelque part que les singletons ne devraient offrir que des services et qu'il est mauvais d'avoir un singleton qui détient un état puisqu'il s'agit essentiellement d'une variable globale à ce moment-là. En fait, il ait plus de sens lorsque j'ai commencé à entrer dans mes tests "Singleton Hell". J'adore singleton, mais c'était juste en train de me faire tomber.
Si je me suis bien compris, vous parlez de la désérialisation correcte du singleton.
Si vous utilisez la sérialisation native .NET, vous pouvez regarder le MSDN Si vous utilisez la sérialisation XML ( Un moyen le plus simple serait de garantir cela dans le setter de votre propriété parente, en accédant à une sorte de cache statique. (Je trouve cette jolie sale, mais c'est un moyen facile de le faire). P> Li>
ol> par exemple: p> et ensuite vous auriez une classe statique avec une sorte de dictionnaire où vous pourriez récupérer rapidement l'instance singleton (ou créer Il n'existe pas si ça n'existe pas). P> Bien que cela puisse fonctionner pour vous, je suis également d'accord avec les autres que c'est rarement le meilleur (ou seulement) moyen de le faire. Il existe sûrement un moyen de refactoriser votre code afin que cela devienne inutile, mais vous devriez fournir des informations supplémentaires sur ce qui est l'utilisation prévue, où se trouve ces données à partir de, ou simplement pourquoi les classes doivent-elles vraiment faire référence à un seul objet. < / p> puisque vous êtes em> en utilisant une cache statique similaire à un singleton, vous devez prendre soin de la mettre en œuvre < fort> correctement fort>. Cela signifie plusieurs choses: p> Votre classe de cache devrait avoir un constructeur privé em>. Vous ne voulez pas que quiconque crée une nouvelle instance explicitement - ce n'est pas une option lors de l'utilisation de singletons. Cela signifie donc que vous devriez exposer une propriété statique publique comme Lorsque tout ce que vous obtenez avoir la même instance, vous pouvez simplement informer le cache pour mettre à jour l'instance privée unique (E.G. appelez Mais il n'est toujours pas effacé de votre exemple comment vous notifiez vos clients que les données ont été mises à jour de toute façon. Un mécanisme axé sur les événements fonctionnerait mieux, car cela vous permettrait de découpler votre code - singletons sont diaboliques . p> p> iserialisable code> exemple. L'exemple montre exactement que - comment remplacer iserializable.getObjectData code> pour renvoyer la même instance sur chaque appel. P> li>
xmlSerializer code>), vous implémentez manuellement ixmlSerializable code> dans la classe
cache.instance code> qui sera toujours em> renvoyer la référence au même objet privé.
Étant donné que le constructeur est privé, vous êtes sûr que seule la classe de cache peut créer l'instance lors de l'initialisation (ou la première mise à jour).
Les détails sur la mise en œuvre de ce modèle peuvent être trouvés sur http://www.yoda.arachsysys.com /cshaarp/singleton.html (qui est également une excellente implémentation de fil-sécurité). P> LI>
cache.update () code> de quelque part). De cette façon, vous pouvez mettre à jour la seule instance que tout le monde utilise. P> li>
ol>
C'est une approche très intéressante car j'ai déjà la "classe statique où je pourrais récupérer rapidement l'instance singleton". S'il vous plaît voir mes commentaires sur la question pour une description plus détaillée de mon problème individuel.
Ouais, les singletons ne sont pas bons. Je comprends que. Je refuse de les utiliser dans mon code à moins que ce ne soit absolument approprié. Cependant, j'en ai une place à l'utilisateur de mon API depuis qu'ils ont dit "Hey James, je continue de perdre la portée du cache et je voudrais un moyen de récupérer le cache." Je leur ai donc donné un singleton pour y accéder. Cependant, je refuse de baser mon API en utilisant le singleton à cause des chutes (que nous ne rentrerons pas ici).
Mon code est en fait découplé car personne ne doit être averti lorsque l'un d'entre eux modifie le cache. Ils maintiennent tout simplement la même référence à un seul objet, et cela fonctionne! Je pense que j'ai peut-être eu ma réponse du début.
La différence avec une implémentation appropriée du modèle singleton est que, en ayant un constructeur privé pour votre objet, vous empêche i> vous-même (ou quelqu'un d'autre) de jamais i> créer une nouvelle instance. Cela ne semble-t-il pas sembler important s'il s'agit d'un petit projet que vous travaillez, mais pourquoi ne pas le faire correctement? Et je vous assure, singleton == Code couplé, même si cela peut sembler différent de votre point de vue (Google it, il y a beaucoup d'exemples et d'articles).
Singleton! = Code couplé. Mon API n'utilise pas le singleton, de sorte que le singleton ne peut pas induire le couplage à l'API. Le singleton existe simplement pour l'utilisateur de l'API. Je veux et besoin de créer plusieurs instances du cache, en particulier à des fins de test. Faire le constructeur privé et baser tout mon code hors du singleton n'est définitivement pas le moyen d'aller dans mon cas.
Peut-être que j'utilise la mauvaise terminologie? Comment appelez-vous un singleton avec un constructeur public? Je devrais probablement dire "une instance unique d'un modèle de classe". Ou quelque chose ...
Nous avons des cas où nous faisons exactement ce dont vous parlez. Nous avons de nombreux objets référençant une instance particulière d'un objet et nous voulons modifier l'instance de l'objet afin que chaque objet faisant référence à l'instance existante, voir le changement.
Le motif que nous suivons est presque ce que vous avez - juste les noms sont différents: p>
Depuis que vous avez copié ma réponse, et je choisis ma réponse pour être correct, je vais vous donner +1: P
Il n'est pas possible de surcharger l'opérateur d'affectation en C # car il serait en C / C ++. Cependant, même si c'était une option, je dirais que vous essayez de résoudre le symptôme et non le problème. Votre problème est que l'attribution d'une nouvelle référence est une nouvelle référence, pourquoi ne pas simplement attribuer les valeurs de lecture à la référence d'origine? et si vous avez peur que d'autres personnes puissent être nouvelles et que d'affecter rendre l'objet dans un singleton ou similaire de sorte qu'il ne puisse pas être modifié après la création, mais la référence restera la même p>
J'avais l'habitude d'être dans cette mentalité exacte (l'idée singleton) jusqu'à ce que je réalisais des singletons sont essentiellement des variables mondiales. En fait, je l'ai récemment refoulé de ne pas être un singleton à cause des problèmes que cela causait pendant mes tests. Ne vous méprenez pas, j'aime Singleton, mais cela a sa place, et c'est pour les objets qui ne conservent pas d'état (ou ont une forme limitée d'état).
Je n'aime pas mon auto singletons. Je travaille actuellement très fort pour obtenir les Devs sur mon projet pour arrêter le spam Singleton pour être honnête. Je l'ai utilisé pour décrire ce que je ment. Il existe d'autres moyens de faire respecter la règle d'une seule instance d'un type donné pour le moment où vous dites que Singletons peut être un cauchemar en matière de test (sans se limiter aux tests)
Dans plusieurs applications basées sur WinForms, j'ai eu besoin de fonctionnalités similaires, dans mon cas pour permettre à un formulaire de saisie de données de travailler sur une copie de l'objet, des informations à partir de laquelle sont copiées sur l'objet d'origine uniquement si l'utilisateur choisit de sauvegarder. Les changements.
Pour faire ce travail, j'ai apporté une idée de mes jours Delphes - la méthode essentiellement, j'ai écrit (bien, ok, généré) Une méthode qui copie des propriétés (et la liste des contenus, etc., etc.) d'une instance à une autre. Cela m'a permis d'écrire du code comme celui-ci: p> modifications apportées dans la boîte de dialogue est privée jusqu'à ce que l'utilisateur choisisse de les enregistrer, puis la variable publique ( un attribue () code>. p> personne code>) est mis à jour. p> attribuer () code> méthode pour personne code> peut ressembler à ceci: p> public Person(Person original)
: this()
{
Assign(original);
}
Je suis plus intéressé par la méthode d'assignation () dont vous parlez. Je n'ai jamais utilisé Delphi, donc je ne comprends pas ce que vous impliquez. J'ai envisagé la réponse «Sainte Graale» pour impliquer une réflexion ou quelque chose de fou que je n'oserais pas utiliser. Je ne peux qu'espérer que c'est là que vous vous dirigez avec votre réponse.
Il est certainement possible d'écrire une version d'attribution () qui utilise la réflexion et permet donc une affectation quasi de la valeur de la propriété entre presque tous les types, mais la performance peut être un problème. Dans mon cas, mes méthodes d'affectation () étaient un code simple. Je vais modifier ma réponse pour clarifier.
Votre méthode d'assignation fait exactement ce que j'ai proposé dans ma question. (Pas que c'est une mauvaise chose .. +1) Je suis heureux que nous soyons sur la même longueur d'onde avec la chose réfléchie. Dans un monde idéal, je pense qu'il y aurait un opérateur spécial pour ce type de mission de valeur, car il est possible avec une réflexion. C'est dommage que c # manque de cette fonctionnalité.
Le problème de l'introduction d'un opérateur spécial est qu'il y a trop de variations sur la sémantique possible. E.G.: Les propriétés devraient-elles être établies avec des configurations privées? Comment gérez-vous des propriétés lues / seulement? Qu'en est-il des propriétés typées comme ilist / ienumérables - partagez-vous la liste ou effectuez une copie? Si une copie, quelle implémentation de l'interface, et que faites-vous sur des événements reconnaissants? Qui possède des objets dans une liste ou une séquence - devrait-il être partagé ou copié? Essentiellement, vous devez connaître le "contexte délimité" (Eric Evans) pour l'objet, qui est une décision de domaine, et non un compilateur.
J'aimerais qu'il y ait une "deuxième meilleure" option de réponse, car toute personne ayant mentionné l'observateur le mérite. Le modèle d'observateur fonctionnerait, mais il n'est pas nécessaire et à mon avis, est surchargé.
Si plusieurs objets doivent conserver une référence au même objet ("myClass", ci-dessous) et vous devez effectuer un affectation em> à l'objet référencé ("myClass"), le moyen le plus simple de gérer est de créer une fonction de valeur de valeur comme suit: p> Observer serait nécessaire Si l'action em> était requise par les objets dépendants au moment de la mission. Si vous souhaitez uniquement conserver la référence, cette méthode est adéquate. Cet exemple ici est identique à celui que j'ai proposé dans ma question, mais je pense que cela met mieux l'accent sur mon intention. P> Merci pour toutes vos réponses. Je les ai sérieusement considérés comme tous. P> p>
Je peux totalement voir où vous venez de cette question. Dans mon cas, l'application clientèle contient un tas de références à un objet - mais cet objet est mis à jour sur le fil afin que la mise à jour soit disponible sur un objet frais. Je ne fais pas / ne peut pas reporter tous les réfs au nouvel objet - il suffit de mettre à jour l'objet existant par la valeur à l'objet qui est venu accrocher le fil. Je suis surpris cette situation n'est pas plus courante.
Curieux, pourquoi implémenter cela avec une fonction et non un constructeur de copie?
WOW, je ne peux pas croire que c'était il y a 4 ans .. La mission de valeur n'est pas très utile dans un constructeur car cela impliquerait de créer une nouvelle référence de l'objet en cours de copie. L'objectif de la mission de valeur est de préserver la référence existante, non pas en créer un nouveau.
Et si vous l'avez fait:
public class B
{
B(int i) {A = new A(i);}
public A A {get; set;}
}
...
void Main()
{
B b1 = new B(1);
A a2 = new A(2);
b1.A = a2;
}
C'est à peu près ce que l'écouteur a suggéré. Un objet intermédiaire.
J'ai eu le même problème. La façon dont j'ai résolu c'était en mettant l'objet que tout se réfère à l'intérieur d'un autre objet et que tout a fait référence à l'objet extérieur. Ensuite, vous pouvez changer l'objet intérieur et tout serait en mesure de référencer le nouvel objet intérieur.
OuterObject.InerObject.stuff
Vous voulez probablement cloner l'objet, essayez Stackoverflow.com/Questtions/78536/cloning-Objects-in- C
Clonage de l'objet et l'affectation Il changera également la référence, de sorte que cela ne fonctionnera pas.
Basé sur les mises à jour de la question, convenue.
Pourriez-vous décrire votre problème? J'ai un modèle similaire avec un objet de configuration, et je reçois simplement que tous les lecteurs rechargent la référence à chaque fois qu'ils ont besoin de saisir un cadre, certes une approche boiteuse.
J'ai plusieurs classes qui partagent une cache. J'ai besoin d'un cache car la récupération des données est extrêmement chère. Plusieurs classes partagent ce cache et l'un d'entre eux doit exécuter "cache = (cache) geterializedCachefromharddrive ()" C'est la même chose que "cache = nouveau cache ()". Les classes qui partagent le cache ne se connaissent pas les unes des autres, il faudrait donc que des solutions de contournement énormes avertissent tout le monde qu'il a changé, comme vous décrivez.
Comment notifiez-vous maintenant? (Ou comment comptez-vous le faire?)
Je travaille autour d'elle à l'heure actuelle en utilisant ValueAsSign. En attribuant les champs individuellement, la référence ne change jamais, je n'ai donc pas à informer quiconque du changement de référence. Peut-être que j'utilise déjà la meilleure approche?
Qu'en est-il de la méthode cache.refreshcachefromhdd () qui fait la même chose, mais empêche l'objet tel qu'il est?
Je travaille sur un problème très similaire ... Stackoverflow.com/Questtions/31707418/...