Przekazywanie parametrów ref i out

Kod umieszczony w temacie będzie w C#, w końcowej części odniosę się do tego, jak to wygląda w VB.NET. Przekazywanie parametrów do funkcji w C# odbywa się poprzez skopiowanie wartości tego parametru. Edytowanie go wewnątrz funkcji, nie zmieni jego wartości na zewnątrz jej.

C#
static void Main(string[] args)
{
 int value = 0;
 Function(value);
 Console.WriteLine(value);
}

static void Function(int value)
{
 value = 4;
}

Jeśli wykonamy powyższy kod, konsola wyświetli nam liczbę 0. Co zrobić, aby zmiana wartości zmiennej value wewnątrz metody Function? I nie możemy użyć, lub nie chcemy użyć return? Z pomocą przychodzą nam dwa słówka kluczowe: ref i out.
W obu przypadkach przekazujemy do metody nie kopię wartości parametru, a jego adres w pamięci. Edytując zmienną wewnątrz metody B zmieniamy jej wartość również w metodzie A, z której metoda B została wywołana. Trochę kodu, aby pokazać o co chodzi.

C# - REF
static void Main(string[] args)
{
 int value = 1;
 Function(ref value);
 Console.WriteLine(value);
}

static void Function(ref int value)
{
 value = value + 3;
}

C# - OUT
static void Main(string[] args)
{
 int value;
 Function(out value);
 Console.WriteLine(value);
}

static void Function(out int value)
{
 value = 4;
}

Można już w kodzie zauważyć pewną różnicę miedzy obydwoma wywołaniami. W przypadku ref inicjalizujemy zmienną od razu przy deklaracji, a dla out inicjalizujemy wewnątrz wywoływanej metody. To jedyny różnica między ref i out.

Podsumowując:
  • używając ref i out przekazujemy tylko adres, pod którym znajduje się wartość
  • używając ref i out nie kopiuje wartości przekazywanych parametrów
  • ref wymaga, aby obiekt był już zainicjalizowany przed przekazaniem go do metody
  • out wymaga, aby obiekt był zainicjalizowany wewnątrz metody, do której został przekazany

Jeszcze jedna uwaga, poniższy kod nie zadziała, mimo, iż mamy obiekt zainicjalizowany przed przekazaniem go, to out wymaga ponownej inicjalizacji wewnątrz metody. W takim przypadku jak ten, wymagane jest użycie słów ref.

C#
static void Main(string[] args)
{
 int value = 1;
 Function(out value);
 Console.WriteLine(value);        
}

static void Function(out int value)
{
 //to nie zadziała
 value = value + 3;
}
A teraz jak wygląda to w VB.NET? W VB.NET nie ma czegoś, co byłoby odpowiednikiem C# out. Przekazywanie przez referencję można wykonać za pomocą takiego kodu:

VB.NET
Sub Main()

 Dim value As Integer
 Func(value)
 Console.WriteLine(value)

End Sub

Sub Func(ByRef value As Integer)

 value += 4

End Sub

VB.NET ma to do siebie, że od razu po zadeklarowaniu zmiennej, inicjalizuje ją. Dlatego też ByRef przekazuje jedynie referencję (adres w pamięci) do obiektu, nie martwiąc się o to, kiedy będzie zainicjalizowany. Jeżeli piszemy bibliotekę w VB.NET, która może być użyta z kodem C# możemy użyć czegoś takiego dla kompatybilności:

VB.NET
Imports System.Runtime.InteropServices

Module Module1

 Sub Main()

  Dim value As Integer
  Func2(value)
  Console.WriteLine(value)

 End Sub

 Sub Func2(<Out> ByRef value As Integer)

  value += 4

 End Sub

End Module

Jeśli spojrzymy jak wygląda kod w IL, w obu przypadkach wywołanie wygląda w ten sam sposób:

call void VBtestowe.Module1::Func(int32&)
call void VBtestowe.Module1::Func2(int32&)

Analogicznie wygląda to dla C#. Jedyną różnicą jest sposób w jaki zadeklarowana jest metoda:

.method public static void  Func(int32& 'value') cil managed
...
.method public static void  Func2([out] int32& 'value') cil managed

[out] informuje kompilator, w którym miejscu ma być zaincjializowana zmienna.
To tyle w tym temacie, jeśli macie jakieś pytania, piszcie w komentarzach.

Komentarze

  1. Cześć.

    Wydajesz się specjalistą od C#. Jest coś takiego jak wskaźnik na funkcję w C? Chcę przekazać do jakiejś funkcji inną funkcję jako argument.

    Dzięki,
    I tak dalej z tym blogiem!

    OdpowiedzUsuń
    Odpowiedzi
    1. Owszem istnieje, polecam zapoznać się z zagadnieniem delegatów w .NET
      Artykuł na MSDN

      Usuń
  2. Bardzo fajny wpis
    Kodowanie to ciężki kawałek chleba...

    OdpowiedzUsuń
  3. Dzięki za wpis :) oszczędziłeś mi trochę szukania. CTS - wcale to nie jest tak ciężki jak się wydaje, wiedzy potrzeba wiele w porównaniu do niektórych zawodów, ale systematyczna pracą da się sporo wywalczyć.

    OdpowiedzUsuń
  4. Szybko wyjaśnione, bardzo fajny poradnikowy wpis. Kodowania trzeba stale się uczyć jak widać, to trzeba po prostu lubić żeby być dobrym programistą.

    OdpowiedzUsuń

Prześlij komentarz

Popularne posty z tego bloga

[C#, VB.NET] Wprowadzanie tylko liczb do TextBoxa

Singleton - wyjaśnienie czym jest

Caroline - player muzyczny ♪♫