C# Tips – typ var
Na prośbę jednego z czytelników kolejny wpis z serii C# tips jest o typie var. Wpis jest trochę dłuższy niż poprzednie, ale też zagadnienie wymaga szerszego opisu.
Jeśli kiedykolwiek miałeś do czynienia z programowaniem w JavaScript na pewno spotkałeś się ze słowem kluczowym var. W języku C# również istnieje takie słowo jednak jego działanie jest zupełnie inne co nie koniecznie jest takie oczywiste przy pierwszym kontakcie. W JavaScript, który jest językiem dynamicznie typowanym, słowo kluczowe var służy do deklaracji zmiennej lokalnej wewnątrz funkcji lub ograniczonej do danej aplikacji przy deklaracji poza funkcjami, nie ma żadnego związku z typem wartości jakie może przechowywać zmienna. Zaś w C#, który jest językiem statycznie typowanym, słowo kluczowe var jest zamiennikiem dla typu zmiennej i podczas kompilacji CLR zamienia je na właściwy typ. Tak więc najprościej mówiąc słowo var jest kolejnym „upiększaczem” kodu jak wyrażenia ? i ?? jednak poza samym skróceniem zapisu ułatwia także proces ewentualnej przyszłej refaktoryzacji.
Na początek przykład użycia słowa kluczowego var:
var a = 2; int i = a;
W powyższym przykładzie przypisanie do zmiennej a wartości 2 sprawi, że w trakcie kompilacji słowo var zostanie podmienione na int i możliwe będzie przypisanie wartości zmiennej a do zmiennej i typu int. Dlatego wbrew temu co można by pomyśleć podczas nauki tego języka, słowo var nie umożliwia zmiany typu zmiennej podczas działania, bo jak już wspomniałem po kompilacji staje się ona już zwyczajną, statycznie typowaną zmienną.
Zalety stosowania var lepiej widać przy korzystaniu z bardziej złożonych typów. Poniżej pokazany typ jest zwracany kiedy korzystając z Linq grupujemy wyniki:
IEnumerable<IGrouping<string, User>> a = usersList.GroupBy(u => u.Group); var b = usersList.GroupBy(u => u.Group);
W pierwszej linii zastosowane standardowe podejście gdzie podany jest typ zmiennej, która przyjmie wartość zwróconą przez funkcję GroupBy (w tym momencie szczegóły z nią związane nie są istotne, wystarczy wiedzieć, że zwraca ona podany typ) wywołaną na liście użytkowników. Jak widać przyjmowany typ jest złożony i jeśli korzystamy z niego w wielu miejscach staje się to uciążliwe.
W kolejnej linii znajduje się równoznaczne wyrażenie, w którym jednak zastosowałem słowo var. Po kompilacji zmienna b również będzie miała typ IEnumerable<IGrouping<string, User>>.
Warto wspomnieć, że podczas korzystania z typu var IntelliSense nie ma problemu z podpowiadaniem składni i pracuje tak jakby zmienna zdefiniowana była z podanym docelowym typem.
Wspomniałem na początku, że var ułatwia refaktoryzację. Pokażę to na przykładzie. Załóżmy, że mamy funkcję, która zwraca wartość typu IEnumerable<string> (interfejs dający dostęp do prostych iteracji, w tym wypadku po kolekcji zmiennych typu string):
IEnumerable<string> F() { return new List<string>(); }
Użyjmy jej teraz korzystając z typu var:
var list = F();
Teraz wyobraźmy sobie, że w pewnym momencie okazuje się, że trafiamy na miejsce gdzie poza iterowaniem po kolekcji zwracanej przez funkcję musimy do niej także coś dodać W takim wypadku można zmienić typ zwracany przez funkcję na ICollection<string>, który poza metodami dostępnymi w IEnumerable daje też dostęp do metod dodających czy usuwających elementy. W przypadku standardowego deklarowania funkcji, gdzie podaje się konkretny typ musielibyśmy zmieniać wszystkie miejsca gdzie definiowaliśmy zmienne przyjmujące wartość zwracaną przez funkcję F(). Jeśli używamy var nie ma takiej konieczności, kompilator sam o to zadba.
Korzystanie z typu var jest jednak nieco ograniczone.
Typ ten może być tylko stosowany w przypadku zmiennych lokalnych definiowanych wewnątrz funkcji, zatem taki kod spowoduje błąd kompilacji:
class Klasa { private var a = new List<string>(); //... }
Drugim ograniczeniem jest konieczność przypisania do zmiennej wartości od razu podczas definicji, bez tego kompilator nie wiedziałby z jakim typem ma mieć do czynienia. Dlatego poniższy kod również zwróci błąd podczas kompilowania:
var a; a = 5;
Jak więc widać słowo kluczowe var powstało głównie po to aby ułatwić pracę programiście i zwolnić go z obowiązku podawania, często złożonych, konkretnych typów zmiennej, co jest szczególnie pomocne przy intensywnym wykorzystaniu typów generycznych i korzystaniu np. z Linq.
EDIT: Małe (albo i nie) sprostowanie muszę napisać. Mianowicie tytuł posta to „typ var”, a nawet z samej dalszej treści wynika, że var nie jest typem, a jedynie słowem kluczowym języka C# i nie należy go nazywać typem. Nie zmieniam jednak tytułu i adresu, aby zachować dotychczas opublikowane linki do tego wpisu.
Skoro jak sam zauważasz, „var” to tylko słowo kluczowe pozwalające kompilatorowi na wydedukowanie typu zmiennej na podstawie tego, co do niej przypisujemy, to czemu w tytule tego wpisu masz „typ var”? Przecież to nie jest żaden typ!
Masz rację somekind, mój błąd. Sugerowałem się tym, że przed napisaniem posta kolega słabo znający C# pytał właśnie o „typ var” i dopiero później po publikacji uświadomiłem sobie, że przecież to jest słowo kluczowe. Niestety teraz jeśli zmienię tytuł to url musiał by zostać i tak z błędem bo inaczej wcześniejsze linki nie będą działać więc chyba ostatecznie dopiszę tylko sprostowanie na końcu.
Var jest również niezbędny przy tworzeniu klas anonimowych.
Witam
Wydaje mi się, że wkradł się błąd. Mianowicie var nie jest zamieniane na właściwy typ podczas kompilacji.
Już podczas inicjalizacji zmiennej ze słowem kluczowym var definiujemy jego typ w zależności od tego jaką wartość mu przypiszemy. Dla przykładu :
public static void Main()
{
var number = 5;
}
Już w momencie inicjalizacji określamy typ var (w tym przypadku int).