Passer des objets par référence ou valeur en C#

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

ainsi, lorsqu'un objet non primitif est passé à une méthode, tout ce qui est fait à l'objet dans la méthode aurait un effet sur l'objet passé. (C# 101 trucs)

cependant, j'ai remarqué que lorsque je passe un système.Dessin.L'Image de l'objet, que ce ne semble pas être le cas? Si je réussis un système.dessin.l'image de l'objet à une autre méthode, et de charger une image sur cet objet, alors laissez cette méthode hors de portée et revenir à l'appel de la méthode, que l'image n'est pas chargée sur l'objet d'origine?

pourquoi?

165
demandé sur nawfal 2012-01-03 10:19:54

7 réponses

objets ne sont pas passés du tout. Par défaut, l'argument est évalué et sa valeur est passée, en valeur, comme 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 - une façon d'accéder à un objet (ou null). Les modifications apportées à cet objet seront visibles de l'appelant. Cependant, changer la valeur du paramètre pour se référer à un objet différent n'est pas être visible lorsque vous utilisez la valeur pass by, Qui est la valeur par défaut pour tous les types.

si vous voulez utiliser pass-by-reference, vous doit utiliser out ou ref , si le type de paramètre est un type de valeur ou un type de référence. Dans ce CAs, effectivement la variable elle - même est passée par référence, de sorte que le paramètre utilise le même emplacement de stockage que l'argument-et change à la paramètre lui-même sont considérés par l'appelant.

:

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 va beaucoup plus en détail dans ce . En gros, "passer par référence" ne veut pas dire ce que tu penses que ça veut dire.

360
répondu Jon Skeet 2012-01-03 06:24:57

un autre exemple de code 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

Testiref: 5

TestObjPlain: test

TestObjRef: TestObjRef

12
répondu vmg 2018-03-12 14:34:01

je suppose que c'est plus clair quand tu le fais comme ça. Je recommande de télécharger LinkPad 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 qui devrait produire

WontUpdate

prénom: Egli, nom: Becerra

Updateimplicitely

prénom: Favio, nom: Becerra

Updateexplicitely

prénom: Favio, nom: Becerra

3
répondu Egli Becerra 2016-09-16 04:05:45

lorsque vous passez l'objet de type System.Drawing.Image à une méthode, vous passez en fait une copie de référence à cet objet.

donc si à l'intérieur de cette méthode vous chargez une nouvelle image vous chargez en utilisant la référence nouvelle/copiée. 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
répondu Haris Hasan 2018-03-12 14:14:20

Comment avez-vous passé l'objet à la méthode?

faites-vous du neuf à l'intérieur de cette méthode pour l'objet? Si c'est le cas, vous devez utiliser ref dans la méthode.

en suivant le lien vous donner une meilleure idée.

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

2
répondu dotnetstep 2018-03-12 14:13:44

Dans le Passage Par Référence de n'ajouter "ref" dans les paramètres de la fonction et un plus de chose que vous devriez déclarer fonction "statique" en raison de main est statique (# public void main(String[] args) )!

namespace preparation
{
  public  class Program
    {
      public static void swap(ref int lhs,ref int rhs)
      {
          int temp = lhs;
          lhs = rhs;
          rhs = temp;
      }
          static void Main(string[] args)
        {
            int a = 10;
            int b = 80;

  Console.WriteLine("a is before sort " + a);
            Console.WriteLine("b is before sort " + b);
            swap(ref a, ref b);
            Console.WriteLine("");
            Console.WriteLine("a is after sort " + a);
            Console.WriteLine("b is after sort " + b);  
        }
    }
}
0
répondu user5593590 2016-04-27 02:07:04

Les accepté de répondre sonne un peu faux et déroutant. Qu'est ce qu'une "copie d'une référence?"

en quoi la déclaration suivante a-t-elle du sens?:

" cependant, changer la valeur du paramètre pour se référer à un objet différent ne sera pas visible lorsque vous utilisez pass by value, qui est la valeur par défaut pour tous les types."Pass by value n'est pas la valeur par défaut pour tous les types.

son exemple dans son lien essaie de définir une instance d'un objet à null. L'objet n'a pas pu être réglé à null à cause de la collecte automatique des ordures. Il ne peut pas être supprimé de cette manière.

voici un article de Microsoft comparant Java et C#.

de https://msdn.microsoft.com/en-us/library/ms836794.aspx

"tous les objets sont des références

Les types de référence

sont très similaires aux pointeurs en C++, en particulier lors de la identifiant d'une nouvelle instance de classe. Mais en accédant aux propriétés ou aux méthodes de ce type de référence, utilisez le "."operator, qui est similaire à l'accès aux instances de données en C++ qui sont créées sur la pile. Toutes les instances de classe sont créées sur le tas en utilisant le nouvel opérateur, mais la suppression n'est pas autorisée, car les deux langues utilisent leurs propres systèmes de collecte des ordures, discutés ci-dessous."

0
répondu user3198764 2016-12-26 10:35:55