Przekazywanie argumentów (obiektów) przez referencję

0

Witam wszystkich.
Pytanie moje jest bardzo proste. Czytając różne mądre teksty w internecie wiem że normalnie argumenty w C# przekazywane są przez wartość, natomiast jeśli chcę przekazać argument przez referencję to trzeba użyć słowa kluczowego ref albo out.

Wszystko fajnie ale przyzwyczajony do Javy zacząłem nieświadomie używać takich konstrukcji: Chcę żeby jedna klasa miała referencję do drugiej więc przekazuję ją np w konstruktorze. Więc przekazując te obiekty w argumentach bez żadnych słów ref czy out zachowują się one jak referencję. Mam coś takiego np:

class Controller
{
  private Klasa Obiekt;
  private Klasa2 Obiekt2;


  public Controller()
  {
    Obiekt = new Klasa();
    Obiekt2 = new Klasa2(Obiekt);
  }
} 

Gdzie Klasa2 przyjmuje w konstruktorze obiekt i na tym obiekcie mogę pracować jakby był współdzielony obiektami Klasy2 i Controllera. Więc jak to jest z tymi refami? Są potrzebne czy nie?

0

Obiekty klas są przekazywane przez referencję. Ogólnie można powiedzieć, że słówko "ref" pozwala Ci wewnątrz metody podstawić nowy obiekt, na który ma wskazywać referencja. Bez tego wywołanie na referencji "new" ogranicza się tylko do zasięgu lokalnego. Kod ilustrujący wyjaśnienie:

 
namespace TestProject
{
    class Program
    {
        static void Main(string[] args)
        {
            Klasa1 klasa = new Klasa1("Main");

            Console.WriteLine("Wartosc property: " + klasa.MyProperty);
            T1(klasa);
            Console.WriteLine("Wartosc property: " + klasa.MyProperty);

            T2(ref klasa);
            Console.WriteLine("Wartosc property: " + klasa.MyProperty);

            Console.ReadKey();
        }

        public static void T1(Klasa1 klasa)
        {
            klasa = new Klasa1("T1");
            Console.WriteLine("Wartosc property wewnatrz T1: " + klasa.MyProperty);
        }

        public static void T2(ref Klasa1 klasa)
        {
            klasa = new Klasa1("T2");
            Console.WriteLine("Wartosc property wewnatrz T2: " + klasa.MyProperty);
        }
    }

    public class Klasa1
    {
        public string MyProperty { get; set; }

        public Klasa1(string item)
        {
            MyProperty = item;
        }
    }
}

W wyniku dostajemy :

 
Wartość property: Main
Wartość property wewnatrz T1: T1
Wartosć property: Main
Wartość property wewnatrz T2: T2
Wartość property: T2
1

Ref przydaje się głównie w przypadku kiedy przekazujemy strukturę (lub typ prosty, jak int) które domyślnie są przekazywane przez wartość.

Osobiście (ale tak robią też np. programiści tworzący bibliotekę standardową) dołączam słówko ref czasami również przy obiektach domyślnie przekazywanych przez referencję (czyli ref nic nie zmienia), żeby zaznaczyć że obiekt zostanie zmodyfikowany przez funkcję.

@up - Do twojego przykładu bardziej by chyba pasowało out zamiast ref.

1
MSM napisał(a)

Osobiście (ale tak robią też np. programiści tworzący bibliotekę standardową) dołączam słówko ref czasami również przy obiektach domyślnie przekazywanych przez referencję (czyli ref nic nie zmienia), żeby zaznaczyć że obiekt zostanie zmodyfikowany przez funkcję.

A potem ktoś zobaczy Twój kod i wrzuci na thedailywtf.
Gdzie niby są te refy w bibliotece standardowej?

out i ref przydają się tylko dla zmiennych typów przekazywanych przez wartość, po to, żeby metoda nie działała na kopii lokalnej tylko na oryginalnej wartości. Dla typów referencyjnych to i tak niczego nie zmieni, więc nie ma sensu.

0

ja widzę zagadnienie tak:
1.) argumenty domyślnie są przekazywane przez wartość (kopia).
Czemu więc czasami zmieniony w metodzie argument jest też zmieniony poza nią, aa czasami nie??
Ano dlatego, że jest różnica w przechowywaniu w pamięci struktur i obiektów.
Struktury zajmują miejsce na stosie i zmienna == struktura. przekazując strukturę do metody przekazujemy jej kopię.
obiekty składowane są na stercie, a na stosie jest referencja do nich (taki wskaźnik). Tutaj zmienna == referencja do obiektu.
Przekazując obiekt do metody tak naprawdę przekazujemy kopię referencji, czyli w metodzie można działać na TYM SAMYM obiekcie.
2.) Słowa kluczowe ref i out mają specjalne znaczenie.
ref pozwala przekazywać referencje do struktur zamiast ich kopii. Dzięki temu można w metodzie zmienić jej wartość.
out jest trochę innego typu. pozwala ono przekazać do metody pustą (null) zmienną w celu zainicjalizowania jej w ciele metody.
Pozdrawiam :)

0

Ech...

Czarodziej0 napisał(a)

jest różnica w przechowywaniu w pamięci struktur i obiektów

Struktura jest czymś "alternatywnym" do klasy. Można mieć obiekty klas i obiekty struktur. Nie ma zatem sensu porównywanie struktur i obiektów.

tak naprawdę przekazujemy kopię referencji, czyli w metodzie można działać na TYM SAMYM obiekcie

Jaka kopia referencji? :| Żadnej kopii nie ma, działa się na oryginale.

out jest trochę innego typu. pozwala ono przekazać do metody pustą (null) zmienną w celu zainicjalizowania jej w ciele metody

Może jeszcze chciałeś napisać, że strukturę o wartości null? [rotfl]

0

owszem jest kopia referencji. Zobacz przykłąd luzika.
w T1 jest przekazana kopia referencji. jej (kopii) zmiana nie ma wpływu na oryginalny obiekt.
w T2 krzekazana jest referencja (oryginalny wskaźnik ze stosu) i jej zmiana powoduje zmianę w metodzie main.

Odnośnie struktur i obiektów wydaje mi się że nie bardzo rozumiesz różnicę między nimi.
A może ja jej nie rozumiem??? Może mi wytłumaczysz???

a słyszałeś o typie:

int? 

i kodzie

int? i = null; 

????

0
Czarodziej0 napisał(a)

owszem jest kopia referencji. Zobacz przykłąd luzika.
w T1 jest przekazana kopia referencji. jej (kopii) zmiana nie ma wpływu na oryginalny obiekt.
w T2 krzekazana jest referencja (oryginalny wskaźnik ze stosu) i jej zmiana powoduje zmianę w metodzie main.

Referencja to nie jest wskaźnik.
W metodach T1 i T2 tworzone są nowe obiekty. Czym będzie różniło się zachowanie tych metod, jeśli zamiast zastępowania obiektu klasa, po prostu zmienimy wartość jego właściwości? O tak:

public static void T1(Klasa1 klasa)
{
    klasa.MyProperty = "T1";
    Console.WriteLine("Wartosc property wewnatrz T1: " + klasa.MyProperty);
}

public static void T2(ref Klasa1 klasa)
{
    klasa.MyProperty = "T2";
    Console.WriteLine("Wartosc property wewnatrz T2: " + klasa.MyProperty);
}
} 

Odnośnie struktur i obiektów wydaje mi się że nie bardzo rozumiesz różnicę między nimi.

Można mieć obiekty struktur i obiekty klas. Na przykład:

 
int x = 32; // definicja obiektu struktury System.Int32
Point p = new Point(1, 2); // definicja obiektu struktury System.Drawing.Point
StringBuilder sb = new StringBuilder("tekst"); // definicja obiektu klasy System.Text.StringBuilder
SqlCommand cmd = new SqlCommand("SELECT * FROM Tabela"); // defincja obiektu klasy System.Data.SqlClient.SqlCommand

x, p, sb, cmd - to wszystko są obiekty. Tak czy nie?

a słyszałeś o typie:

int? 

Tyle, że Nullable<int> to nie jest to samo, co int. Znasz inne struktury niż Nullable<T>, które pozwalają przypisać wartość null?

0
somekind napisał(a)

Referencja to nie jest wskaźnik.

Weź nie żartuj...
A czym się różnią???

somekind napisał(a)

W metodach T1 i T2 tworzone są nowe obiekty. Czym będzie różniło się zachowanie tych metod, jeśli zamiast zastępowania obiektu klasa, po prostu zmienimy wartość jego właściwości? O tak:

public static void T1(Klasa1 klasa)
{
    klasa.MyProperty = "T1";
    Console.WriteLine("Wartosc property wewnatrz T1: " + klasa.MyProperty);
}

public static void T2(ref Klasa1 klasa)
{
    klasa.MyProperty = "T2";
    Console.WriteLine("Wartosc property wewnatrz T2: " + klasa.MyProperty);
}
} 

Już nie wiem czy mówimy o przykładzie luzika, czy Twoim na potrezeby własnych racji...

somekind napisał(a)

Można mieć obiekty struktur i obiekty klas. Na przykład:

 
int x = 32; // definicja obiektu struktury System.Int32
Point p = new Point(1, 2); // definicja obiektu struktury System.Drawing.Point
StringBuilder sb = new StringBuilder("tekst"); // definicja obiektu klasy System.Text.StringBuilder
SqlCommand cmd = new SqlCommand("SELECT * FROM Tabela"); // defincja obiektu klasy System.Data.SqlClient.SqlCommand

x, p, sb, cmd - to wszystko są obiekty. Tak czy nie?

Zgadza się. to wszystko są obiekty.
Tylko że struktury są specyficznym typem obiektów przechowywanych na stosie, co ma znaczenie przy przekazywaniu argumentów do metody.

somekind napisał(a)
Czarodziej0 napisał(a)

a słyszałeś o typie:

int? 

Tyle, że Nullable<int> to nie jest to samo, co int. Znasz inne struktury niż Nullable<T>, które pozwalają przypisać wartość null?

owszem nie, nie to samo, ale nadal to struktura.
Każda struktura może być Nullable<T>. Zajrzyj do dokumentacji.

A sformułowanie obiekt struktury... hmmm... ciekawe, ciekawe...

1
Czarodziej0 napisał(a)
somekind napisał(a)

Referencja to nie jest wskaźnik.

Weź nie żartuj...
A czym się różnią???

Hmm... chyba tym, że nie są tym samym.
Referencja wskazuje miejsce obiektu typu referencyjnego w pamięci, a wskaźniki pozwalają operować na zmiennych typów wartościowych. Oprócz tego udostępniają arytmetykę i umożliwiają poruszanie się po pamięci, do tego wymagają bloków unsafe i kompilacji z takim też parametrem.

Już nie wiem czy mówimy o przykładzie luzika, czy Twoim na potrezeby własnych racji...

Nigdy jeszcze nie miałem potrzeby zastępować obiektu przekazanego do metody (bo niby czemu to miałoby służyć i po co tak robić?). Dlatego też nie byłem nawet świadomy jak zadziała metoda T1. Referencja to adres w pamięci, zatem operacje w metodzie dotyczą tego, co znajduje się pod tym adresem. Gdy przekazujemy zmienną typu referencyjnego do metody, to będziemy operowali (poprzez metody i właściwości) na tym samym obiekcie, który jest poza metodą, bez względu na to, czy użyjemy ref czy nie. Zgadza się?
Natomiast w dość egzotycznym przypadku, z jakim mamy do czynienia w metodzie T1, tworzony jest w niej lokalnie nowy obiekt, zatem przydzielany jest nowy obszar w pamięci i lokalna "klasa" referuje właśnie nie niego, natomiast poza metodą referencja pozostaje bez zmian. W T2 natomiast referencja przychodząca w parametrze metody jest nadpisywana. Tak przynajmniej to rozumiem, ale oczywiście mogę nie mieć racji, może Ty lepiej wyjaśnisz to zachowanie?
Tak czy siak trzeba o takim niuansie wiedzieć i pamiętać.

Zgadza się. to wszystko są obiekty.

Czyli się zgadzamy. :)

A sformułowanie obiekt struktury... hmmm... ciekawe, ciekawe...

Jednak nie? :(

Chyba nie nadążam za zmiennością Twojego nastroju.
Natomiast można porównywać klasy ze strukturami, ale porównywanie obiektów ze strukturami jest bez sensu.
Można powiedzieć, że marchewka jest tańsza i mniej trwała od butów. Ale nie ma sensu porównywać sklepu obuwniczego z marchewką.

owszem nie, nie to samo, ale nadal to struktura.

JEDYNA struktura, której można przypisać null.
A Ty napisałeś, że:

out jest trochę innego typu. pozwala ono przekazać do metody pustą (null) zmienną w celu zainicjalizowania jej w ciele metody

Więc w ogólnym przypadku Twoje twierdzenie nie jest prawdziwe, a dywagacje na temat Nullable za wiele nie wnoszą. Out po prostu pozwala na przekazanie do metody niezainicjalizowanej zmiennej (w przeciwieństwie do ref, które inicjalizacji wymaga).
A co do niezainicjalizowanych zmiennych, to nawet jakiś czas temu na forum była dyskusja w tej kwestii i jak się okazało, to nawet niezainicjalizowane zmienne typów referencyjnych nie mają wartości null. Po prostu są niezainicjalizowane.

Zajrzyj do dokumentacji.

Tym razem policzyłem do dziesięciu. Ale to jest rada na poziomie trolla z forum onetu. Na przyszłość daruj sobie. Dyskutujemy tutaj, ok?

0

Wydaje ni się, że nasza dyskusja nie ma sensu.
Ja Ciebie nie przekonam, a Ty mnie. A raczej też nikomu nie pomaga.

Oczywiście są różnice między wskaźnikiem a referencją, ale są one bardzo małe.
Często mówię, że referencja to taki ładnij obudowany wskaźnik, ale to jest moje zdanie.

To, że Ty nie miałeś potrzeby zastępowanie obiektów w ciele metody nie znaczy że inni jej nie mieli.
Są słowa kluczowe ref i out isą one nie bez powodu. I zanczą co innego (aczkolwiek podobnego).

POruwnywanie obiektów ze strukturami jako takimi oczywiście nie ma większego sensu,
ale należy zdawać sobie sprawę, że są inaczej traktowane przez kompilator.
Móisz obiekt struktury, a czy też używasz np. obiekt wyjątku, czy obiekt atrybutu???
Przecież to też są obiekty.

Pozdrawiam

0

Nie mam zamiaru Ciebie do niczego przekonywać. Po prostu nie lubię nieścisłości i wprowadzania w błąd, dlatego też prostuje Twoje posty.

Różnice między wskaźnikami a referencjami nie są wcale bardzo małe. Wskaźniki i referencje do czego innego służą i inaczej się zachowują.
To jak pralka i lodówka. Między nimi też jest bardzo mała różnica?

Czarodziej0 napisał(a)

ale należy zdawać sobie sprawę, że są inaczej traktowane przez kompilator.

Co jest inaczej traktowane? Struktury i obiekty? Czy struktury i klasy? I jesteś pewien, że przez kompilator a nie runtime?

Móisz obiekt struktury, a czy też używasz np. obiekt wyjątku, czy obiekt atrybutu???
Przecież to też są obiekty.

Wyjątki i atrybuty są klasami.
Nie mówię "obiekt struktury", użyłem tego sformułowania tutaj po to, aby móc sprostować Twoją wypowiedź o tym, że "obiekty i struktury są inaczej przechowywane w pamięci", która jest nieprecyzyjna. Bo to obiekty typów wartościowych i referencyjnych są inaczej przechowywane w pamięci.

0

Wracając do sedna dyskusji, znalazłem zwięzłe porównanie "ref" i "out" (http://www.c-sharpcorner.com/UploadFile/puranindia/Parameterpassing06012009025036AM/Parameterpassing.aspx)

 What is the difference between ref and out parameter modifiers?

ref:

    *
      ref is a mechanism of parameter passing by reference

    *
      A variable to be sent as ref parameter must be initialized.

    *
      ref is bidirectional i.e. we have to supply value to the formal parameters and we get back processed value.

 out:

    *
      Out is also a mechanism of parameter passing by reference

    *
      A variable to be sent as out parameter don't need to be initialized

    *
      Out is a unidirectional i.e. we don't supply any value but we get back processed value.

 

1 użytkowników online, w tym zalogowanych: 0, gości: 1