web-dev-qa-db-fra.com

Passage d'objets par référence ou valeur en C #

En C #, j'ai toujours pensé que les variables non primitives étaient passées par référence et les valeurs primitives passées par valeur.

Ainsi, lorsque vous transmettez à une méthode un objet non primitif, toute action effectuée sur l'objet de la méthode affectera l'objet transmis. (Trucs C # 101)

Cependant, j'ai remarqué que lorsque je passe un objet System.Drawing.Image, cela ne semble pas être le cas? Si je passe un objet system.drawing.image à une autre méthode et charge une image sur cet objet, puis laisse cette méthode hors de portée et retourne à la méthode d'appel, cette image n'est pas chargée sur l'objet d'origine?

Pourquoi est-ce?

209
michael

Les objets ne sont pas passés du tout. Par défaut, l'argument est évalué et sa valeur est transmise valeur par valeur en tant que valeur initiale du paramètre de la méthode que vous appelez. Maintenant, le point important est que la valeur est une référence pour les types de référence - un moyen d’obtenir un objet (ou null). Les modifications apportées à cet objet seront visibles de l'appelant. Cependant, changer la valeur du paramètre pour faire référence à un objet différent ( ne sera pas visible lorsque vous utiliserez pass par, ce qui est la valeur par défaut pour tous les types .

Si vous voulez utiliser le mot de passe par référence, vous devez utiliser out ou ref, que le type de paramètre soit un type de valeur ou un type de référence. Dans ce cas, la variable elle-même est effectivement transmise par référence. Le paramètre utilise donc le même emplacement de stockage que l'argument. Les modifications apportées au paramètre lui-même sont alors visualisées par l'appelant.

Alors:

public void Foo(Image image)
{
    // This change won't be seen by the caller: it's changing the value
    // of the parameter.
    image = Image.FromStream(...);
}

public void Foo(ref Image image)
{
    // This change *will* be seen by the caller: it's changing the value
    // of the parameter, but we're using pass by reference
    image = Image.FromStream(...);
}

public void Foo(Image image)
{
    // This change *will* be seen by the caller: it's changing the data
    // within the object that the parameter value refers to.
    image.RotateFlip(...);
}

J'ai un article qui entre dans beaucoup plus de détails dans ceci . Fondamentalement, "passer par référence" ne signifie pas ce que vous pensez que cela signifie.

439
Jon Skeet

Un exemple de code supplémentaire pour illustrer ceci:

void Main()
{


    int k = 0;
    TestPlain(k);
    Console.WriteLine("TestPlain:" + k);

    TestRef(ref k);
    Console.WriteLine("TestRef:" + k);

    string t = "test";

    TestObjPlain(t);
    Console.WriteLine("TestObjPlain:" +t);

    TestObjRef(ref t);
    Console.WriteLine("TestObjRef:" + t);
}

public static void TestRef(ref int i)
{
    i = 5;
}

public  static void TestPlain(int i)
{
    i = 5;
}

public static void TestObjRef(ref string s)
{
    s = "TestObjRef";
}

public static void TestObjPlain(string s)
{
    s = "TestObjPlain";
}

Et la sortie:

TestPlain: 0

TestRef: 5

TestObjPlain: test

TestObjRef: TestObjRef

18
vmg

Je suppose que c'est plus clair quand vous le faites comme ça. Je recommande de télécharger LinqPad pour tester des choses comme celle-ci.

void Main()
{
    var Person = new Person(){FirstName = "Egli", LastName = "Becerra"};

    //Will update egli
    WontUpdate(Person);
    Console.WriteLine("WontUpdate");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateImplicitly(Person);
    Console.WriteLine("UpdateImplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateExplicitly(ref Person);
    Console.WriteLine("UpdateExplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");
}

//Class to test
public class Person{
    public string FirstName {get; set;}
    public string LastName {get; set;}

    public string printName(){
        return $"First name: {FirstName} Last name:{LastName}";
    }
}

public static void WontUpdate(Person p)
{
    //New instance does jack...
    var newP = new Person(){FirstName = p.FirstName, LastName = p.LastName};
    newP.FirstName = "Favio";
    newP.LastName = "Becerra";
}

public static void UpdateImplicitly(Person p)
{
    //Passing by reference implicitly
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

public static void UpdateExplicitly(ref Person p)
{
    //Again passing by reference explicitly (reduntant)
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

Et cela devrait produire

WontUpdate

Prénom: Egli, Nom: Becerra

Mettre à jour implicitement

Prénom: Favio, Nom: Becerra

Mettre à jour explicitement

Prénom: Favio, Nom: Becerra

6
Egli Becerra

Lorsque vous transmettez l'objet de type System.Drawing.Image à une méthode, vous transmettez en réalité une copie de la référence à cet objet.

Donc, si à l'intérieur de cette méthode, vous chargez une nouvelle image, vous utilisez une nouvelle référence/copie. Vous ne faites pas de changement dans l'original.

YourMethod(System.Drawing.Image image)
{
    //now this image is a new reference
    //if you load a new image 
    image = new Image()..
    //you are not changing the original reference you are just changing the copy of original reference
}
3
Haris Hasan

Comment avez-vous passé objet à méthode?

Faites-vous nouveau dans cette méthode pour objet? Si c'est le cas, vous devez utiliser ref dans la méthode.

Le lien suivant vous donne une meilleure idée.

http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html

2
dotnetstep