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#
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
C# - OUT
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:
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#
VB.NET
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
Jeśli spojrzymy jak wygląda kod w IL, w obu przypadkach wywołanie wygląda w ten sam sposób:
Analogicznie wygląda to dla C#. Jedyną różnicą jest sposób w jaki zadeklarowana jest metoda:
[out] informuje kompilator, w którym miejscu ma być zaincjializowana zmienna.
To tyle w tym temacie, jeśli macie jakieś pytania, piszcie w komentarzach.
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.
Cześć.
OdpowiedzUsuń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!
Owszem istnieje, polecam zapoznać się z zagadnieniem delegatów w .NET
UsuńArtykuł na MSDN
Bardzo fajny wpis
OdpowiedzUsuńKodowanie to ciężki kawałek chleba...
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ńSzybko wyjaśnione, bardzo fajny poradnikowy wpis. Kodowania trzeba stale się uczyć jak widać, to trzeba po prostu lubić żeby być dobrym programistą.
OdpowiedzUsuń