Po co używać słowa kluczowego " ref " podczas przekazywania obiektu?

Jeśli przekazuję obiekt metodzie, dlaczego powinienem używać słowa kluczowego ref? Czy to nie jest domyślne zachowanie?

Na przykład:

class Program
{
    static void Main(string[] args)
    {
        TestRef t = new TestRef();
        t.Something = "Foo";

        DoSomething(t);
        Console.WriteLine(t.Something);
    }

    static public void DoSomething(TestRef t)
    {
        t.Something = "Bar";
    }
}


public class TestRef
{
    public string Something { get; set; }
}

Wyjściem jest "Bar", co oznacza, że obiekt został przekazany jako odniesienie.

Author: Jim Fell, 2008-10-09

11 answers

Podaj ref jeśli chcesz zmienić obiekt:

TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef t)
{
  t = new TestRef();
  t.Something = "Not just a changed t, but a completely different TestRef object";
}

Po wywołaniu DoSomething, t nie odnosi się do oryginału new TestRef, ale odnosi się do zupełnie innego obiektu.

Może to być również przydatne, jeśli chcesz zmienić wartość obiektu niezmiennego, np. a string. Nie można zmienić wartości string po jej utworzeniu. Ale za pomocą ref, można utworzyć funkcję, która zmienia ciąg znaków na inny, który ma inny wartość.

Nie jest dobrym pomysłem używać ref, chyba że jest to konieczne. Użycie ref daje metodzie swobodę zmiany argumentu na coś innego, wywołujące metodę będą musiały być zakodowane, aby upewnić się, że obsługują tę możliwość.

Również jeżeli parametr typ jest obiektem, wtedy zmienne obiektu zawsze działają jako odniesienia do obiektu. Oznacza to, że gdy używane jest słowo kluczowe ref, masz odniesienie do odniesienia. Pozwala to na wykonywanie rzeczy opisanych w przykład podany powyżej. Jeśli jednak typ parametru jest wartością pierwotną (np. int), to jeśli parametr ten jest przypisany do metody, to wartość argumentu, który został przekazany zostanie zmieniona po zwróceniu metody:

int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10

void Change(ref int x)
{
  x = 5;
}

void WillNotChange(int x)
{
  x = 10;
}
 306
Author: Scott Langham,
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
2020-08-18 13:01:42

Musisz odróżnić "przekazywanie odniesienia przez wartość" od "przekazywanie parametru / argumentu przez odniesienie".

Napisałem dość długi artykuł na ten temat Aby uniknąć konieczności dokładnego pisania za każdym razem, gdy pojawia się to na grupach dyskusyjnych

 88
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
2020-08-18 13:02:12

W. NET gdy przekazujesz dowolny parametr do metody, tworzona jest kopia. W typach wartości oznacza, że wszelkie modyfikacje wprowadzone do wartości znajdują się w zakresie metody i są tracone po zakończeniu metody.

Podczas przekazywania typu odniesienia, wykonywana jest również kopia, ale jest to Kopia odniesienia, tzn. teraz masz w pamięci dwa odniesienia do tego samego obiektu. Jeśli więc użyjesz referencji do modyfikacji obiektu, zostanie on zmodyfikowany. Ale jeśli zmodyfikować samo odniesienie - musimy pamiętać, że jest to Kopiuj - wtedy wszelkie zmiany są również tracone po wyjściu z metody.

Jak już ludzie mówili, przypisanie jest modyfikacją odnośnika, więc jest stracone:

public void Method1(object obj) {   
 obj = new Object(); 
}

public void Method2(object obj) {  
 obj = _privateObject; 
}

Powyższe metody nie modyfikują oryginalnego obiektu.

Mała modyfikacja twojego przykładu

 using System;

    class Program
        {
            static void Main(string[] args)
            {
                TestRef t = new TestRef();
                t.Something = "Foo";

                DoSomething(t);
                Console.WriteLine(t.Something);

            }

            static public void DoSomething(TestRef t)
            {
                t = new TestRef();
                t.Something = "Bar";
            }
        }



    public class TestRef
    {
    private string s;
        public string Something 
        { 
            get {return s;} 
            set { s = value; }
        }
    }
 59
Author: Ricardo Amores,
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
2014-11-17 09:06:30

Ponieważ TestRef jest klasą (które są obiektami odniesienia), możesz zmienić zawartość wewnątrz t bez przekazywania jej jako ref. Jeśli jednak zdasz t jako ref, TestRef może zmienić to, do czego odnosi się oryginalny T. to znaczy, że wskazuje na inny obiekt.

 18
Author: Ferruccio,
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
2008-10-09 11:53:32

Z ref możesz napisać:

static public void DoSomething(ref TestRef t)
{
    t = new TestRef();
}

I t zostaną zmienione po zakończeniu metody.

 16
Author: Rinat Abdullin,
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
2015-07-26 20:39:28

Pomyśl o zmiennych (np. foo) typów referencyjnych (np. List<T>) jako o przechowywaniu identyfikatorów obiektów w postaci "Object #24601". Załóżmy, że polecenie foo = new List<int> {1,5,7,9}; powoduje, że foo przechowuje "Object #24601" (listę zawierającą cztery pozycje). Następnie wywołanie foo.Length zapyta obiekt #24601 o jego długość i odpowie 4, więc foo.Length będzie równe 4.

Jeśli foo zostanie przekazana do metody bez użycia ref, metoda ta może wprowadzić zmiany w obiekcie # 24601. W konsekwencji takich zmian, foo.Length może już nie równa się 4. Jednak sama metoda nie będzie w stanie zmienić foo, która będzie nadal trzymać "Object #24601".

Podanie foo jako parametru ref pozwoli wywołanej metodzie wprowadzić zmiany nie tylko do obiektu #24601, ale także do samego foo. Metoda może utworzyć nowy obiekt #8675309 i zapisać odniesienie do niego w foo. Jeśli tak się stanie, foo nie będzie już posiadać "obiektu # 24601", lecz "obiektu #8675309".

W praktyce Typ odniesienia zmienne Nie przechowują łańcuchów w postaci "Object #8675309"; nie przechowują nawet niczego, co może sensownie przekształcić się w liczbę. Mimo że każda zmienna typu referencyjnego będzie zawierała pewien wzorzec bitowy, nie ma stałej zależności między wzorzec bitowy przechowywany w takich zmiennych a obiektami, które identyfikują. Nie ma możliwości, aby Kod mógł wyodrębnić informacje z obiektu lub odniesienia do niego, a następnie określić, czy inne odniesienie zidentyfikowało ten sam obiekt, chyba że kod posiadali lub znali odniesienie, które zidentyfikowało oryginalny obiekt.

 8
Author: supercat,
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
2015-07-17 16:30:51

To jest jak przekazywanie wskaźnika do wskaźnika w C. W. NET pozwoli Ci to zmienić to, do czego odnosi się oryginalny T, osobiście chociaż myślę, że jeśli robisz to w. NET, prawdopodobnie masz problem z projektem!

 5
Author: pezi_pink_squirrel,
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
2008-10-09 11:58:03

Używając słowa kluczowego ref z typami referencji skutecznie przekazujesz odniesienie do referencji. Pod wieloma względami jest to to samo, co użycie słowa kluczowego out, ale z niewielką różnicą, że nie ma gwarancji, że metoda rzeczywiście przypisze cokolwiek do parametru ref'ed.

 4
Author: Isak Savo,
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
2008-10-09 11:53:39

ref naśladuje (lub zachowuje się) jako obszar globalny tylko dla dwóch zakresów:

  • rozmówca
  • Callee.
 3
Author: guneysus,
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
2017-10-15 22:58:46

Jeśli przekazujesz wartość, jednak rzeczy są inne. Możesz wymusić przekazanie wartości przez odniesienie. Pozwala to na przykład przekazać liczbę całkowitą do metody, a metoda może zmodyfikować liczbę całkowitą w Twoim imieniu.

 1
Author: Andrew,
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
2008-10-09 11:51:31

Ref oznacza, czy funkcja może dostać się do samego obiektu, czy tylko do jego wartości.

Przekazywanie przez odniesienie nie jest związane z językiem; jest to strategia wiążąca parametr obok pass-by-value, pass by name, pass by need itp...

Uwaga boczna: nazwa klasy {[0] } jest w tym kontekście paskudnie złym wyborem ;).

 1
Author: xtofl,
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
2008-10-09 11:58:07