C# Automatique de la copie en profondeur de struct
J'ai un struct, MyStruct
, qui a un membre privé private bool[] boolArray;
et une méthode ChangeBoolValue(int index, bool Value)
.
J'ai une classe, MyClass
, qui a un champ public MyStruct bools { get; private set; }
Quand je crée un nouveau MyStruct objet à partir d'un existant, puis d'appliquer la méthode ChangeBoolValue(), le bool array dans les deux objets est changé, parce que la référence, et non ce qui a été mentionné, a été copié dans le nouvel objet. E. g:
MyStruct A = new MyStruct();
MyStruct B = A; //Copy of A made
B.ChangeBoolValue(0,true);
//Now A.BoolArr[0] == B.BoolArr[0] == true
Est-il un moyen de forcer une copie de mettre en œuvre une meilleure copie, ou est-il un moyen de mettre en œuvre ce qui n'aura pas le même problème?
J'avais faite spécialement MyStruct une struct parce que c'était le type de valeur, et je n'ai pas voulu multiplication des références.
Merci, la réponse n'y est décrit essentiellement le problème que j'ai 😛
OriginalL'auteur 3Pi | 2012-07-05
Vous devez vous connecter pour publier un commentaire.
Le runtime effectue une rapide copie de la mémoire de leurs structures et autant que je sache, il n'est pas possible d'introduire ou de la force de votre propre procédure de copie. Vous pouvez introduire votre propre
Clone
méthode ou même un constructeur par copie, mais vous ne pouvaient pas faire valoir qu'ils les utilisent.Votre meilleur pari, si possible, pour faire de votre structure immuable (ou immuable de la classe) ou de refonte en général pour éviter ce problème. Si vous êtes le seul consommateur de l'API, alors peut-être vous pouvez simplement rester très vigilant.
Jon Skeet (et d'autres) ont décrit ce problème et, même s'il peut y avoir des exceptions, d'une manière générale: les structures mutables sont le mal.
Peut les structures qui contiennent des champs de types de référence
OriginalL'auteur Chris Sinclair
Une méthode simple pour faire une (profonde) de copier, mais pas le plus rapide (car il utilise la réflexion), est d'utiliser
BinaryFormatter
pour sérialiser l'objet original à unMemoryStream
et puis désérialiser à partir de ceMemoryStream
à une nouvelleMyStruct
.Fonctionne pour les classes et les structures.
MyStruct
appeler leDeepCopy
méthode lors du passage autour des instances. Je pense que 3Pi se demandant comment faire la copie en profondeur automatiquement chaque fois que l'exécution des copies de la struct.Depuis la cession crée un alias (dans son code
object.ReferenceEquals(A, B)
est vrai), tout simplement en utilisant l'opérateur d'affectation ne permettra pas d'obtenir ce qu'il cherche. Je ne suis pas sûr qu'il y en est une autre manière de l'aide d'un (extension) de la méthode.Oui, c'est ce que je voulais dire - a été l'espoir de ne pas avoir à changer quoi que ce soit à l'exception de la structure elle-même.
les structures sont de type valeur. Ne veut pas dire que lorsque vous affectez 1 à l'autre, qu'il est copié? Si non, qu'est-ce que signifie pour un struct être le type de valeur?
Je ne suis pas sûr que ce soit possible. L'exécution rapide de la mémoire de la copie de leurs structures et (autant que je sache) il n'y a pas de méthode pour mettre en œuvre/remplacer ce comportement. @EricJ Ouais, vous êtes sur le point à ce sujet. Mais encore une fois, d'avoir une méthode d'extension sera juste une commodité pour le consommateur. Si vous avez le contrôle sur l'utilisation de
MyStruct
, 3Pi, alors que ce devrait être bon, vous avez juste besoin d'être vigilant quant à son utilisation.OriginalL'auteur Eric J.
Comme une solution de contournement, je vais mettre en œuvre les suivantes.
Il y a 2 méthodes dans la structure qui permet de modifier le contenu de
BoolArray
. Plutôt que de créer de la matrice lorsque la structure est copié, BoolArray sera créé à nouveau lors d'un appel au changement c'est fait, comme suitBien que ce serait mauvais pour les utilisations qui a impliqué beaucoup de changement de la BoolArray, mon utilisation de la structure est beaucoup de copie, et très peu en train de changer. Cela ne fera que modifier la référence à la matrice lorsqu'un changement est nécessaire.
J'ai fait parti de l'immuable struct, comme suggéré par Chris. Elle exige le moins de changement externe, et peu de changement interne. C'était une idée qui m'est venue, mais elle aurait été malodorante code.
OriginalL'auteur 3Pi
Pour éviter bizarre sémantique, toute structure qui détient un champ d'une mutable type de référence doit faire une de deux choses:
Comme d'autres l'ont noté, une façon de permettre à une structure à simuler un tableau serait pour elle de tenir un tableau, et de faire une nouvelle copie de ce tableau toutes les fois qu'un élément est modifié. Une telle chose serait, bien sûr, être extrêmement lente. Une approche alternative serait d'ajouter un peu de logique pour stocker les indices et les valeurs de la dernière quelques mutations de la demande; tout moment, une tentative est faite pour lire le tableau, vérifiez si la valeur est l'une des récemment écrites et, le cas échéant, utiliser la valeur stockée dans la structure au lieu de celui de la matrice. Une fois que tous les "machines à sous" à l'intérieur de la structure sont remplis, faire une copie du tableau. Cette approche serait, au mieux, "seulement" offre une vitesse constante jusqu'rapport à la régénération de la matrice si des mises à jour a frappé de nombreux éléments différents, mais pourrait être utile si la très grande majorité des mises à jour de frapper un petit nombre d'éléments.
Une autre approche lorsque les mises à jour sont susceptibles d'avoir une forte concentration spéciale, mais a frappé trop grand nombre d'éléments pour eux de s'inscrire dans une struct, serait de conserver une référence à un "principal" tableau, ainsi que d'un "mises à jour" tableau avec un entier indiquant quelle partie du tableau principal "mises à jour" tableau représente. Des mises à jour nécessitent souvent la régénération de la "mises à jour" array, mais cela pourrait être beaucoup plus petit que le tableau principal; si le
"mises à jour" tableau devient trop grande, le tableau principal peut être régénéré avec les changements représenté par les "mises à jour" matrice constituée en son sein.
Le plus gros problème avec l'une de ces approches est que, bien que la
struct
pourrait être conçu de manière à présenter cohérence de la valeur de type sémantique tout en permettant la copie, un coup d'œil à la structure du code ferait à peine évidente (par rapport à la plaine-vieux-données de structures, d'où le fait que la structure dispose d'un champ appeléFoo
est très clair commentFoo
va se comporter).OriginalL'auteur supercat
Je pensais à un problème similaire liées à des types de valeur, et a trouvé une "solution" à ce. Vous voyez, vous ne peut pas modifier le constructeur de copie par défaut en C# comme vous pouvez le faire en C++, car il est conçu pour être léger et effets secondaires-gratuit. Cependant, ce que vous pouvez faire est d'attendre jusqu'à ce que vous réellement accès à la structure, et de vérifier ensuite si elle a été copié.
Le problème, c'est qu'à la différence de types de référence, les structures n'ont pas de véritable identité; il est seulement par la valeur de l'égalité. Cependant, ils doivent toujours être stockés à un certain endroit dans la mémoire, et cette adresse peut être utilisée pour identifier (même temporairement) un type de valeur. La GC est une préoccupation ici, parce qu'il peut déplacer des objets, et donc modifier l'adresse à laquelle la structure est situé, de sorte que vous devez être en mesure de faire face à cette situation (par exemple, faire de la structure de données du privé).
Dans la pratique, l'adresse de la structure peut être obtenue à partir de la
this
de référence, parce que c'est un simpleref T
dans le cas d'un type de valeur. Je laisse le moyens pour obtenir l'adresse d'une référence à ma bibliothèque, mais c'est assez simple à émettre personnalisé CIL. Dans cet exemple, je crée quelque chose de ce qui est essentiellement un byval tableau.Comme vous pouvez le voir, ce type de valeur se comporte exactement comme si le sous-jacent tableau a été copié vers un nouvel emplacement à chaque fois que la référence au tableau est copié.
ATTENTION!!! Utilisez ce code uniquement à vos propres risques, et, de préférence, jamais dans un code de production. Cette technique est mal et le mal à de nombreux niveaux, car il suppose d'identité pour quelque chose qui ne devrait pas l'avoir. Bien que cette méthode tente de "faire respecter" type de la valeur sémantique de cette structure ("la fin justifie les moyens"), il y a certainement de meilleures solutions pour le problème réel dans presque tout cas. Veuillez également noter que, bien que j'ai essayé de prévoir prévisibles des problèmes avec cela, il pourrait y avoir des cas où ce type de tout à fait un comportement inattendu.
OriginalL'auteur IllidanS4