Przekazywanie obiektów przez odniesienie lub wartość w C#

W C# zawsze myślałem, że nie-prymitywne zmienne są przekazywane przez odniesienia i prymitywne wartości przekazywane przez wartość.

Tak więc, gdy przekazujemy do metody dowolny Nie-prymitywny obiekt, wszystko, co robimy z obiektem w metodzie, wpłynie na przekazywany obiekt. (C # 101 stuff)

Jednak zauważyłem, że kiedy przejdę System.Rysunek.Obiekt obrazu, że tak się nie wydaje? Jeśli zdam system.rysunek.obiektu obrazu do innej metody i załadować obraz na ten obiekt, a następnie niech ta metoda wyjdzie poza zakres i wrócić do metody wywołującej, że obraz nie jest załadowany na oryginalny obiekt?

Dlaczego tak jest?

Author: nawfal, 2012-01-03

7 answers

Obiekty w ogóle nie są przekazywane. Domyślnie argument jest obliczany, a jego wartość jest przekazywana jako wartość początkowa parametru metody, którą wywołujesz. Teraz ważne jest to, że wartość jest referencją dla typów referencyjnych - sposobem na dotarcie do obiektu (lub null). Zmiany w tym obiekcie będą widoczne z wywołującego. Jednak zmiana wartości parametru na odnoszącą się do innego obiektu spowoduje, że nie będzie widoczna podczas używania wartość pass by, która jest domyślna dla wszystkich typów .

Jeśli chcesz użyć pass-by-reference, musisz użyć out lub ref, niezależnie od tego, czy parametr type jest typem wartości, czy typem odniesienia. W takim przypadku, efektywnie sama zmienna jest przekazywana przez referencję, więc parametr używa tej samej lokalizacji przechowywania co argument - a zmiany w samym parametrze są postrzegane przez wywołującego.

Więc:

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(...);
}

Mam artykuł, który idzie o wiele więcej szczegóły w tym . Zasadniczo, "pass by reference" nie oznacza tego, co myślisz, że to znaczy.

 360
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-03 06:24:57

Jeszcze jedna próbka kodu do zaprezentowania tego:

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";
}

I wyjście:

TestPlain:0

TestRef:5

TestObjPlain: test

TestObjRef: TestObjRef

 12
Author: vmg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 14:34:01

Myślę, że jest jaśniejszy, gdy robisz to w ten sposób. Polecam pobranie LinkPad , aby przetestować takie rzeczy.

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";
}

I to powinno wyjść

WontUpdate

Imię: Egli, nazwisko: Becerra

Updateimplicite

Imię: Favio, nazwisko: Becerra

Updateexplicite

Imię: Favio, nazwisko: Becerra

 3
Author: Egli Becerra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 04:05:45

Kiedy przekazujesz obiekt typu System.Drawing.Image do metody, przekazujesz kopię odniesienia do tego obiektu.

Więc jeśli wewnątrz tej metody ładujesz nowy obraz, ładujesz go za pomocą nowego/skopiowanego odniesienia. Nie zmienisz oryginału.

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
Author: Haris Hasan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 14:14:20

Jak przekazałeś obiekt method?

Czy robisz nową wewnątrz tej metody dla obiektu? Jeśli tak, musisz użyć ref w metodzie.

Następujący link daje lepszy pomysł.

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

 2
Author: dotnetstep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-12 14:13:44

W Pass By Reference dodajesz tylko "ref" w parametrach funkcji i jeden kolejną rzeczą, którą powinieneś zadeklarować jako "static", ponieważ main to static (#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
Author: user5593590,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-27 02:07:04

Przyjęta odpowiedź brzmi trochę źle i myląco. Co to jest " kopia referencji?"

W jaki sposób poniższe stwierdzenie ma sens?:

" jednak zmiana wartości parametru na inny obiekt nie będzie widoczna, gdy używasz wartości pass by, która jest domyślna dla wszystkich typów."Wartość Pass by nie jest domyślna dla wszystkich typów.

Jego przykład w linku próbuje ustawić instancję obiektu na null. Obiekt nie został pomyślnie ustawiona na null z powodu automatycznego usuwania śmieci. Nie można go usunąć w ten sposób.

Oto artykuł Microsoft porównujący Javę i C#.

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

" Wszystkie obiekty są referencjami

Typy referencji są bardzo podobne do wskaźników w C++, szczególnie podczas ustawiania identyfikatora jakiejś nowej instancji klasy. Ale podczas uzyskiwania dostępu do Właściwości lub metod tego typu odniesienia, użyj"."operator, który jest podobny do dostępu do instancji danych w C++, które są tworzone na stosie. Wszystkie instancje klas są tworzone na stercie za pomocą nowego operatora, ale usuwanie nie jest dozwolone, ponieważ oba języki używają własnych schematów usuwania śmieci, omówionych poniżej."

 0
Author: user3198764,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-26 10:35:55