Ref & Out

Wszyscy piszemy metody, używamy tam parametrów i nie przywiązujemy większej uwagi do tego co się z nimi tak naprawdę dzieję. Szczególnie na początku naszej koderskiej wędrówki mało interesuje nas ich los.

Sam jeszcze do niedawna byłem ignorantem w tym temacie. Przyszła kryska na Matyska, zapoznałem się ze słówkami kluczowymi Ref i Out którymi możemy wzbogacić parametry wejściowe metod i dziś to właśnie o nich chciałbym z Tobą porozmawiać.

O typach wartościowych i referencyjnych

Jak dobrze wiemy możemy wyróżnić dwa typy w C# – wartościowe oraz referencyjne. Różnią się one między innymi miejscem przechowywania danych. W przypadku tych pierwszych jest to stos (typ referencyjny), drugi zawodnik siedzi na stercie (typ referencyjny).

Typ wartościowy – przetrzymuje jedną wartość (cyfrową, bitową bądź logiczną). Podczas przypisywania zmiennej operacja odbywa się za pomocą kopiowania.

int a = 10;
int b = a; //KOPIOWANIE

Typ referencyjny – przetrzymuje zbiór informacji określany obiektem. Podczas przypisywania tworzymy referencję do tego samego obiektu.

ExampleClass ec = new ExampleClass();
ExampleClass ec2 = ec; // REFERENCJA do ec

Ref i Out

Powyższe słówka kluczowe mogą być dla nas pomocne przy przekazywaniu referencji do metod. W wielkim skrócie można by o nich powiedzieć dosłownie tyle:

  • Ref – działa dwustronnie. Przekazuje wartość i referencję, można ją zmodyfikować i wraca do nas.
  • Out – działa jednostronnie. Przekazuję referencję, wartość musimy zainicjalizować. Modyfikujemy bezpośrednio referencję.

Najlepiej będzie nam je zrozumieć w aplikacji konsolowej.

Verba docent, exempla trahunt.

Zdefiniujmy 3 inty, pierwszy z nich oznaczmy jako outsideInt, do kolejnych dodajmy Ref i Out na końcu by łatwo je rozróżnić.

Zdefiniujmy 3 metody działające praktycznie tak samo – dodają 10 do wejściowego parametru. W pierwszy przypadku będziemy przekazywać prosty typ wartościowy, w drugim okrasimy go słówkiem ref w ostatnim out.

using System;

namespace CodeForBlog
{
    class Program
    {
        static void Main(string[] args)
        {
            int outsideInt = 20;
            int outsideIntRef = 20;
            int outsideIntOut = 20;
             
            SomeMethod(outsideInt);
            SomeMethodRef(ref outsideIntRef);
            SomeMethodOut(out outsideIntOut);

            Console.WriteLine(outsideInt);
            Console.Read();
        }

        //What is going out with insideInt?

        //value
        private static void SomeMethod(int insideInt)
        {
            insideInt += 10;
        }

        //reference + value
        private static void SomeMethodRef(ref int insideInt)
        {
            insideInt += 10;
        }

        //reference
        private static void SomeMethodOut(out int insideInt)
        {
            insideInt = 0;
            insideInt += 10;
        }
    }
}

Podglądnijmy co się stanie z naszymi outsideInt’ami po opuszczeniu metod.

 

  • Przekazanie samej wartości (int) w metodzie SomeMethod skutkuje przekazaniem kopii która wewnątrz metody osiągnie wartość 30, ale po zamknięciu klamer tyle ją widzieli – outsideInt pozostanie równy 20.
  • Przekazanie wartości i referencji (ref) skutkuje tym, że metoda SomeMethodRef zadziała dwustronnie i odda nam naszego zmodyfikowanego inta więc outsideIntRef osiągnie wartość 30 (po wyjściu z metody).
  • Przekazanie samej referencji (out) skutkuje tym, że musimy zainicjalizować ją w ciele metody (stąd zapis insideInt = 0). Z tego faktu wewnątrz ciała metody otrzymuje 0 + 10 = 10 i ta wartość zostaje w referencji przekazana do outsideIntOut = 10.

Ten prosty przykład uświadamia jak to wszystko działa. Mam nadzieję, że będzie dla Ciebie pomocny jeśli wcześniej temat wartości i referencji przyprawiał o zakłopotanie.

Pozdrawiam i do następnego! Wojtek