Przejdź do głównej zawartości

[C#] Jaka jest różnica między value types a reference types?


Przyjrzyjmy się różnicom między value types a reference types.

Najpierw spróbujmy je sobie wyobraźić w bardziej życiowej sytuacji, bez programistycznego bełkotu.

Value type to "informacja sama w sobie". Taką daną mogłaby być na przykład karteczka z zapisanym czyimś numerem telefonu. Jeśli znajomy poprosi nam o przekazanie mu tego numeru, weźmiemy inną karteczkę i zapiszemy na niej ten sam numer. Jeśli z nudów zaczniemy bazgrać na naszej karteczce i zmienimy jej treść, karteczka naszego znajomego pozostanie bez zmian.

Reference type to ciężki wolumen w bibliotece, zawierający dużo danych. Możemy poprosić bibliotekarza o informację, gdzie możemy te dane znaleźć. Da nam on wtedy karteczkę z tytułem dzieła i numerem rzędu, gdzie się ono znajduje. Jeśli znajomy poprosi nas o pomoc i okaże się, że potrzebne mu są informacje z tej samej książki, łatwiej będzie skopiować treść naszej karteczki i wręczyć ją znajomemu, niż kopiować tysiącstronicowy wolumen. Z drugiej strony, jeśli my postanowimy napisać coś głupiego na marginesie jednej ze stron, nasz znajomy zobaczy to, kiedy będzie przeglądał książkę.

Tak właśnie działają value types i reference types.
  • value type jest daną samą w sobie, reference type jest odnośnikiem do danych - adresem pamięci, gdzie możemy je znaleźć
  • przypisanie zmiennej value type do innej zmiennej tworzy kopię danych, zaś w przypadku reference type kopiujemy tylko odnośnik do pamięci - obiekt w niej zapisany pozostaje taki sam - teraz odnoszą się do niego dwie referencje, nie jedna
  • value types przechowywane są na stosie, zaś reference types na stercie. W tym poście nie będę się o tym rozpisywać - więcej informacji można znaleźć tutaj [under construction]
  • Garbage Collector zajmuje się tylko zwalnianiem pamięci zajmowanej przez reference types. więcej o Garbage Collectorze można poczytać tutaj [under construction]
Do value types zaliczają się:
  • struktury
  • enumy
  • typy proste - liczbowe, bool i char
Zaś reference types to:
  • klasy
  • interfejsy
  • delegaty (więcej o delegatach tutaj [under construction])
  • stringi (każdy string to obiekt klasy string) 
  • tablice (każda tablica dziedziczy z klasy System.Array)
Przyjrzyjmy się dokładniej jak działa przypisywanie dla value types:

    class Program
    {
        static void Main(string[] args)
        {
            int valueType = 1;
            int valueTypeCopy = valueType;
            valueTypeCopy = 2;
            Console.WriteLine(valueType); 
            //wypisze się 1, bo wartość valueType została niezmieniona, gdyż valueTypeCopy to kopia

            Console.ReadKey();
        }
    }

A tak działa to dla reference types:

    class NumberBox
    {
        public int Number;
    }

    class Program
    {
        static void Main(string[] args)
        {
            var referenceType = new NumberBox { Number = 1 };
            var referenceTypeCopy = referenceType;
            referenceTypeCopy.Number = 2;
            Console.WriteLine(referenceType.Number);
            //wypisze się 2, bo referenceTypeCopy odnosi się do tego samego obiektu co referenceType

            Console.ReadKey();
        }
    }

Trochę podstępnie zachowuje się string. Skoro to reference type, powinien działać tak samo jak każda inna klasa. Ale czy tak jest?

    class Program
    {
        static void Main(string[] args)
        {
            string referenceType = "abc";
            string referenceTypeCopy = referenceType;
            referenceTypeCopy = "def";
            Console.WriteLine(referenceType);
            //wypisuje się "abc". Dlaczego? 

            Console.ReadKey();
        }
    }

Dzieje się tak, ponieważ w C# string jest immutable - a to oznacza, że jego wartość nie może zostać zmieniona po utworzeniu obiektu. Dlatego każda modyfikacja lub przypisanie stringa de facto tworzy nowy obiekt klasy string i przypisuje jego adres do starej referencji. W linijce w której do referenceTypeCopy przypisujemy referenceType tworzony jest nowy obiekt zajmujący inne miejsce w pamięci niż stary, dlatego zmiana treści referenceTypeCopy nie wpływa na treść referenceType. Więcej można poczytać o tym tutaj [under construction].

Komentarze

  1. Ten przykład z karteczką z numerem oraz karteczką z koordynatami w bibliotece - genialny. Ciężko będzie to zapomnieć :)

    OdpowiedzUsuń

Prześlij komentarz