web-dev-qa-db-fra.com

Quand une valeur / un objet C # est-elle copiée et quand sa référence est-elle copiée?

Je reçois sans cesse le même problème où un objet que je veux référencer est copié ou où un objet que je veux copier est référencé. Cela se produit lorsque j'utilise l'opérateur =.

Par exemple, si j'envoie l'objet sous une autre forme, c'est-à-dire:

SomeForm myForm = new SomeForm();
SomeObject myObject = new SomeObject();
myForm.formObject = myObject;

... puis modifiez l'objet dans le formulaire, l'objet d'origine n'est pas modifié. C'est comme si l'objet était copié et non référencé. Pourtant, quand je fais cela:

SomeObject myObject = new SomeObject();
SomeObject anotherObject = new SomeObject();
anotherObject = myObject;

... puis modifiez anotherObject, myObject est également modifié.

Le cas le plus aggravant est lorsque j'essaie de cloner un de mes objets définis:

public class SomeObject
{
    double value1, value2;

    //default constructor here

    public SomeObject(val1, val2)
    {
        value1 = val1;
        value2 = val2;
    }

    public void Clone(SomeObject thingToCopy)
    {
        this.value1 = thingToCopy.value1;
        this.value2 = thingToCopy.value2;
    }
}

quand je fais ça ...

SomeObject obj1 = new SomeObject(1, 2);
SomeObject obj2 = new SomeObject();
obj2.Clone(obj1);

...obj1 est référencé et toute modification de obj2 changements obj1.

Objets système tels que int, double, string, etc. semblent toujours être copiés, sauf dans le cas de la méthode de clonage ci-dessus.

Ma question est, sans tenir compte de l'utilisation du mot-clé ref dans les fonctions, quand un objet est-il copié et quand un objet est-il référencé dans tous les cas (par exemple lors du passage à des fonctions, lors de la définition comme d'autres objets (comme les deux premiers exemples ci-dessus), lors de la copie de variables membres comme le troisième exemple, etc.)?

63
Mike Webb

Il est difficile de répondre précisément à ce genre de question sans passer énormément de temps à choisir soigneusement vos mots.

Je l'ai fait dans quelques articles qui peuvent vous être utiles:

Cela ne veut pas dire que les articles sont parfaits, bien sûr - loin de là - mais j'ai essayé d'être aussi clair que possible.

Je pense qu'une chose importante est de séparer les deux concepts (passage de paramètres et types de référence vs valeur) dans votre tête.

Pour regarder vos exemples spécifiques:

SomeForm myForm = new SomeForm();
SomeObject myObject = new SomeObject();
myForm.formObject = myObject;

Cela signifie que myForm.formObject et myObject font référence à la même instance de SomeObject - comme deux personnes ayant des feuilles de papier distinctes, chacune ayant la même adresse écrite dessus. Si vous allez à l'adresse sur un morceau de papier et peignez la maison en rouge, puis allez à l'adresse sur le deuxième morceau de papier, vous verrez une maison rouge.

Il n'est pas clair ce que vous entendez par "puis modifiez l'objet dans le formulaire" car le type que vous avez fourni est immuable. Il n'y a aucun moyen de modifier l'objet lui-même. Vous pouvez changer myForm.formObject pour faire référence à une autre instance de SomeObject, mais c'est comme griffonner l'adresse sur une feuille de papier et y écrire une adresse différente. Cela ne changera pas ce qui est écrit sur l'autre feuille de papier.

Si vous pouviez fournir un programme court mais complet dont vous ne comprenez pas le comportement (idéalement une application console, juste pour garder les choses plus courtes et plus simples), ce serait être plus facile de parler des choses en termes concrets.

47
Jon Skeet

Salut Mike Tous les objets, qui dérivent de ValueType, tels que struct ou d'autres types primitifs sont des types de valeur. Cela signifie qu'ils sont copiés chaque fois que vous les affectez à une variable ou les passez en tant que paramètre de méthode. D'autres types sont des types de référence, ce qui signifie que lorsque vous attribuez un type de référence à une variable, ce n'est pas sa valeur, mais son adresse dans l'espace mémoire est affectée à la variable. Vous devez également noter que vous pouvez passer un type de valeur comme référence à l'aide du mot clé ref. Voici la syntaxe

public void MyMethod(ref int a) { a = 25 }
int i = 20;
MyMethod(ref i); //Now i get's updated to 25.

J'espère que ça aide :)

7
Davita

En ce qui concerne le clonage de vos objets si les valeurs que vous copiez d'un objet à un autre sont des types de référence, toute modification de ces valeurs dans l'objet d'origine affectera les valeurs de l'objet copié (car ce ne sont que des références au même objet)

Si vous devez cloner un objet qui possède des propriétés qui sont des types de référence, vous devez soit rendre ces types clonables, soit en faire une copie manuelle en instanciant de nouvelles instances selon les besoins.

Considérez utiliser l'interface IClonable bien que ce ne soit pas la meilleure des solutions à mon humble avis.

1
RobV