Kurs C# cz. 3 – Klasy
UWAGA! Już teraz sprawdź NOWY, JESZCZE LEPSZY kurs C#, którzy przygotowałem. Znajdziesz go po TYM linkiem.
Witam w trzeciej już części kursu, który podobno ma Was nauczyć programować w języku C#. Do tej pory mogliście zobaczyć jak wygląda ogólnie proces tworzenia nowego projektu, napisaliście swój pierwszy Hello World i nauczyliście się w jaki sposób możecie przechowywać w waszych programach dane liczbowe czy znakowe. Tym razem przejdę do zagadnienia związanego z klasami i typami referencyjnymi. Jest to trochę inne podejście niż w sporej części kursów już dostępnych w internecie gdzie klasy omawiane są bardzo późno. O ile takie kursy jakoś tam sprawdzają się jeśli chodzi o język C++ gdzie bez klas można spokojnie przez dłuższy czas coś jak najbardziej funkcjonalnego tworzyć to jednak w przypadku takich języków jak Java czy właśnie C#, które od początku rzucają programiście klasy i obiekty pod nogi uważam, że wprowadzenie tego zagadnienia już w tym momencie będzie odpowiednie. Poza tym z racji, że C# opiera się na klasach i obiektach zrozumienie przynajmniej podstawowych pojęć z tym związanych pozwoli dopiero tak naprawdę programować w tym języku, w końcu nawet Hello World nie mógł się obyć bez napisania przynajmniej jednej klasy. Przeczytanie tej części pozwoli Ci też drogi czytelniku ocenić jak łatwo przychodzi Ci zrozumienie obiektowości i pewnego poziomu abstrakcji z nią związanego. Bo z doświadczenia wiem, że różne osoby mają z tym mniejszy lub większy problem, problematyczne jest to zwłaszcza dla osób, które wcześniej programowały np. strukturalnie w językach takich jak C. Tak samo jak w poprzednich dwóch częściach również tutaj temat poruszony będzie tak naprawdę wstępnie, bo, zwłaszcza w tym wypadku, przekazanie wszystkich informacji z tym zagadnieniem związanych spowodowałoby, że po pierwsze wpis miałby gigantyczną objętość, a po drugie musiałbym też co chwilę zahaczać o inne zagadnienia, aby część rzeczy móc przedstawić.
1. Klasa? A na co to komu?
Z wytłumaczeniem czym jest klasa jest taki problem, że ciężko znaleźć nawet dla niech definicję, która byłaby zrozumiała dla osoby, która klas nie zna (taka rekurencja niewiedzy :P ) lub dopiero zaczyna wgryzać się w ogóle w programowanie i nie zna jeszcze wszystkich typowych dla tej dziedziny pojęć. Jedna z formułek mówi, że klasa jest do definicja dla obiektów, która określa jakie „cechy” posiada dany typ obiektów. Mówiąc bardziej obrazowo i pokazując to na bardziej życiowym przykładzie to np. jeśli uznamy, że klasa „Samochód”, mówi, że obiekty tej klasy posiadają silnik, cztery koła, kierownicę i fotel oraz potrafią się poruszać do przodu i do tyłu, skręcać i hamować to jeśli na parkingu powiemy, że „ten obiekt jest klasy ‘Samochód’” to wiemy, że posiada wymienione wcześniej cechy. Inna definicja klasy mówi, że jest to pewnego rodzaju szablon do tworzenia obiektów danego typu. Jest ona również prawdziwa jednak nie lubię jej za bardzo tylko z powodu użycia słowa „szablon”, które np. dla osób programujących też w C++ może kojarzyć się z czymś zupełnie innym. Ale faktycznie można powiedzieć, że tak jak z użyciem jakiegoś szablonu wycinamy różne, jednakowo wyglądające kształty tak samo dzięki zdefiniowanej klasie tworzymy obiekty posiadające ten sam zestaw cech. Mówiąc więc już bardziej „informatycznie” klasa pozwala zdefiniować nasz własny, nowy typ danych, którego to typu będziemy mogli potem tworzyć zmienne i obiekty.
Niby bez klas w programowaniu da się żyć, ale co to za życie? Nie mówię oczywiście, że programowanie obiektowe to jedyna słuszna droga niemniej uważam, że klasy i obiekty są chyba najnaturalniejszym odzwierciedleniem „rzeczywistości” w językach programowania. Tzn. pozwalają zamodelować otaczające nas rzeczy w kodzie nadawać im parametry/właściwości/cechy oraz definiować sposób ich działania, interfejs jakim się „komunikują” z użytkownikiem itd. Są moim zdaniem dosyć intuicyjnym sposobem „grupowania” luźnych wartości w logiczny byt i całość.
2. Niech stanie się klasa!
Teraz dodamy do projektu nową klasę, którą z czasem będziemy uzupełniać. Ponieważ wcześniej w tekście posługiwałem się przykładami z samochodem to proponuję aby i tym razem nasza klasa modelowała w dużym uproszczeniu samochód.
Otwórz projekt z poprzednich części i dodaj do niego plik z nową klasą. Możesz to zrobić wybierając prawym przyciskiem myszy nazwę projektu w oknie Solution Explorer, potem Add->New item…, znajdź na liście klasę C#, podaj jej nazwę i wybierz Add. Gotowe, w Twoim projekcie pojawił się nowy plik z pustą klasą.
namespace Kurs { class Samochod { } }
Żeby klasa była w jakikolwiek sposób użyteczna dodajmy do niej publiczne pola. Pola są to zmienne znajdujące się wewnątrz klasy. O zmiennych mogłeś poczytać w poprzedniej części kursu. A o co chodzi z publicznymi polami? Publiczne, czyli poprzedzone słowem kluczowym public, pola są polami dostępnymi dla wszystkich, a więc takimi, które po utworzeniu obiektu klasy będzie można zmieniać poza nią. Poza polami publicznymi mogą być również takie oznaczone jako prywatne bądź chronione. O polach chronionych powiem więcej przy innej okazji, zaś pola prywatne są to zmienne, które może modyfikować tylko nasza klasa, a więc nie ma do nich dostępu z poza jej poziomu.
No dobra to jak wygląda dodawanie pól do klasy? Robi się to w prosty sposób – deklarując zmienne wewnątrz klasy. Dodajmy więc dla naszego samochodu takie publiczne pola jak rok produkcji, marka i pojemność silnika:
namespace Kurs { class Samochod { public int RokProdukcji; public string Marka; public float PojemnoscSilnika; } }
Użyłem tutaj przy okazji typu zmiennej, którego jeszcze nie omawiałem, mianowicie stringa. Zmienne typu string służą do przechowywania ciągu tekstowego.
Nasza pierwsza klasa jest już gotowa i możemy od teraz tworzyć obiekty typu Samochod.
3. Zróbmy sobie obiekt
Przejdź teraz do funkcji main(), która jest już z nami od dłuższego czasu, i w której po poprzedniej części kursu powinieneś mieć dodaną jedną zmienną liczbową:
using System; namespace Kurs { class Program { static void Main(string[] args) { int zmienna = 2 + 2; Console.WriteLine(zmienna); } } }
Sama deklaracja zmiennej referencyjnej jaką jest zmienna trzymająca obiekty jest identyczna jak deklaracja zmiennej wartościowej, a więc
typ unikalnaNazwa;
Tak więc zadeklarujmy pod istniejącą zmienną kolejną, tym razem typu Samochod:
using System; namespace Kurs { class Program { static void Main(string[] args) { int zmienna = 2 + 2; Samochod autko; Console.WriteLine(zmienna); } } }
W tym momencie pojawia się pierwsza różnica między zmiennymi wartościowymi, a zmiennymi referencyjnymi. Te pierwsze jeśli nie poda im się od razu wartości przyjmują wartość domyślną zależną od typu (np. 0 dla typu int), zaś zmienne referencyjne przed przypisaniem im obiektu nie mają ustawionej wartości, albo mówiąc inaczej mają wartość null. Null jest kolejnym słowem kluczowym w C# i oznacza właśnie brak wartości, albo inaczej „pustą wartość”. Nulla można przypisać do zmiennej referencyjnej jeśli chcemy pozbyć się z niej obiektu, który wcześniej tam siedział. Ok, ale jak do cholery przypisać zmiennej nowy obiekt?! Do tego celu posłużymy się słowem kluczowym new i utworzymy obiekt typu Samochod, który przypiszemy do zmiennej autko:
using System; namespace Kurs { class Program { static void Main(string[] args) { int zmienna = 2 + 2; Samochod autko = new Samochod(); Console.WriteLine(zmienna); } } }
Od teraz zmienna autko posiada obiekt klasy Samochod, z którego publicznych pól możemy korzystać. Oprócz tworzenia nowych obiektów możemy je również przypisywać z jednej zmiennej do drugiej, jeśli obie zmienne są tego samego typu. Ale po co są te nawiasy za nazwą klasy? Jakaś funkcja tutaj jest czy co?! W pewnym sensie tak, jest tutaj swojego rodzaju funkcja, a konkretnie konstruktor klasy, w tym wypadku domyślny. W ten oto sposób dochodzimy do kolejnego zagadnienia związanego z klasami czyli do jej konstruktorów.
4. Konstruujący konstruktor
Konstruktory klas są to specyficzne metody w naszych klasach, których celem jest wykonanie pewnych operacji podczas tworzenia obiektu danej klasy. Wiem, że jeszcze nie byliśmy przy temacie metod jako takich jednak bardzo ogólnie wspomniałem o nich na początku kursu i wierzę, że zrozumiecie o co chodzi ;) Przykładowo jeśli chcemy, aby podczas tworzenia pola w naszym obiekcie miały nadawane jakieś konkretne wartości możemy to zrobić właśnie w konstruktorze. Konstruktory w przeciwieństwie do normalnych funkcji nie mogą zwracać żadnej wartości. Konstruktor zawsze ma taką nazwę jak sama klasa. Tak więc jeśli chcesz przykładowo tworząc obiekt naszej klasy Samochod ustawić mu dla roku produkcji wartość 2015 to będzie to wyglądało w następująco:
namespace Kurs { class Samochod { public Samochod() { RokProdukcji = 2015; } public int RokProdukcji; public string Marka; public float PojemnoscSilnika; } }
Jak widać konstruktor też w tym wypadku jest publiczny i w większości przypadków właśnie taki będzie bo tylko wtedy można za jego pomocą utworzyć obiekt w podany wcześniej sposób.
Konstruktorów w klasie może być kilka jednak muszą mieć one różne parametry tak, żeby kompilator mógł jednoznacznie określić jakiego chcesz użyć. Więc jeśli będziemy chcieli móc albo skorzystać z konstruktora przypisującego wartość 2015 dla roku produkcji, albo przypisującego podaną przez nas w parametrze wartość to klasa będzie miała poniższą postać:
namespace Kurs { class Samochod { public Samochod() { RokProdukcji = 2015; } public Samochod(int rokProdukcji) { RokProdukcji = rokProdukcji; } public int RokProdukcji; public string Marka; public float PojemnoscSilnika; } }
Jednak w jaki sposób użyć konstruktora z parametrem? Wystarczy podać wartość przy korzystaniu z new. Dlatego jeśli teraz będę chciał podać przy tworzeniu obiektu inną wartość dla roku produkcji to wystarczy, że utworzę ten obiekt w ten sposób:
Samochod autko = new Samochod(2014);
Jednak pozostaje jeszcze pytanie jak to możliwe, że mogliśmy utworzyć obiekt bez pisania bezparametrowego konstruktora? Było to możliwe dlatego, że w języku C# istnieje coś takiego jak konstruktor domyślny, który nie ustania wartości pól klasy, a pozwala po prostu utworzyć „czysty” obiekt naszej klasy, jednak kiedy sami napiszemy bezparametrowy konstruktor wtedy konstruktor domyślny przestaje być używany, jednak jeśli dopiszemy naszej klasie jedynie konstruktor z parametrami to nadal możemy korzystać z domyślnego konstruktora.
5. Daj mi swoje pola
Dobra, to już prawie koniec tej części kursu. Ale nie może w niej zabraknąć czegoś co zapewne nurtuje niektórych od samego początku – jak do cholery dobrać się do tych pól klasy, które napisaliśmy?!
Tutaj sprawa jest raczej prosta. Żeby dostać się do zmiennej będącej polem utworzonego obiektu klasy wystarczy posłużyć się operatorem kropki. Konstrukcja jest następująca:
obiekt.zmienna;
Obiekt w powyższym zapisie to zmienna referencyjna, która przechowuje obiekt naszej klasy, zaś zmienna to publiczne pole klasy, do którego chcemy się dobrać. Potem wszystko wygląda już tak jak przy zwykłych zmiennych. Dlatego mając nasz program możemy wypisać na ekranie rok produkcji ustawiony w obiekcie autko w taki oto sposób:
using System; namespace Kurs { class Program { static void Main(string[] args) { int zmienna = 2 + 2; Samochod autko = new Samochod(2014); Console.WriteLine(autko.RokProdukcji); } } }
Jeśli chciałbyś teraz ustawić naszemu autku markę to możesz to zrobić tak:
autko.Marka = "Marka";
Przy okazji możesz zobaczyć jak wygląda przypisywanie tekstu do zmiennej typu string.
To na tyle w trzeciej części kursu języka C#. Widzimy się w części czwartej, już niedługo będziecie mogli pisać programy, które nie będą zawierały samych zmiennych ;)
Prosimy o dalsze części kursu !! :))
Hehe, rozumiem, że czekacie na kolejne części i one oczywiście się pojawią, jednak w tym momencie ciężko mi podawać konkretne terminy (spokojnie, nie pojawią się one w bardzo dalekiej przyszłości) bo mam aktualnie bardzo dużo różnych spraw do zrobienia i nawet nie mam kiedy siąść przed następnymi wpisami.
Panie Marku, doceniam to, że prowadzi pan swój internetowy kurs.Mam tylko pytanie, w linijce z kodem „main(string[] args)” co oznacz lub ma oznaczać „args”?
string[] args są to parametry przekazywane do aplikacji przy uruchamianiu. Czyli gdyby uruchamiać nasz program np. z linii komend i wpisać 'program.exe 123′ to to '123′ właśnie znalazłoby się w args.
Już trochę czasu minęło a kursów brak :(
Witam Panie Marku chciałbym się dowiedzieć czy będzie kontynuował Pan kurs C#. Nie ukrywam że jest fajnie napisany przystępnym językiem i fajnie jakby była możliwość aby kontynuował Pan swoje lekcje ;)
Widzę, że zainteresowanie kursem jest spore :) Chyba już kiedyś albo na blogu albo na fb wspomniałem, że ogólnie miałem przez jakiś okres mało czasu żeby siąść do jakiegokolwiek tekstu, tym bardziej do kursu, który wymaga więcej pracy. Ale ze względu na dużo pytań o kontynuację postaram się jak najszybciej dostarczyć kolejne części, bo nie ukrywam cieszy mnie, że mogę komuś przekazać swoją wiedzę :)
Witam kurs przeczytałem i przeanalizowałem ze 3razy żeby utrwalić pojęcia i czekam niecierpliwie na nowe części kursu ;)
Miło mi to czytać :) Aktualnie pojawiła się już część 4, a nowe są w przygotowaniu także zachęcam do obserwowania bloga ;)
Czemu jak wpisuje klasę do programu to podkreśla mi czerwoną linią samochod?
Jak pokażesz kawałek kodu, który stwarza problem to postaramy się coś poradzić ;) Najlepiej cały plik gdzie tę klasę dodajesz
kod programu: kod sprawiajacy problem:
using System; Samochod autko = new Samochod(2014);
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
int zmienna = 2 + 2;
Samochod autko = new Samochod(2014);
Console.WriteLine(zmienna);
Console.ReadLine();
}
}
}
klasa:
namespace Kurs
{
class Samochod
{
public int RokProdukcji = 2014;
public string Marka;
public float PojemnoscSilnika;
}
}
Jak dobrze widzę to w programie wywołując new Samochod(2014) chcesz korzystać z konstruktora, który przyjmuje jako parametr jedną liczbę, ale w klasie Samochod nie dodałeś własnego konstruktora, który przyjmował by taki parametr. Istnieje tylko konstruktor domyślny, który nie przyjmuje parametrów.
Dodaj w klasie Samochód np. taki konstruktor:
public Samochod(int rok)
{
RokProdukcji = rok;
}
I powinno śmigać
A, w którym miejscu wstawić konstruktor?
W tym miejscu w klasie Samochód „deklaruje” Pan wartość RokProdukcji jako taką samą jak wartość rokProdukcji (czyli 2015), ale pomimo tego, gdy użyję Console.WriteLine(autko.RokProdukcji); zwracana jest wartość 0 zamiast tych 2015, chociaż dla samego rokProdukcji wszystko jest dobrze. Dopiero jak dopiszę kod deklarujący wartość rokProdukcji w klasie samochód jako 2015, taka wartość jest zwracana. Czy w moim rozumowaniu jest gdzieś błąd?
Nie wiem czy to ja jestem taki ułomny ale kompletnie nic nie rozumiem z tych klas :( Nawet nie mogę napisać czego konkretnie gdyż od samego początku do końca nic nie jest dla mnie jasne…. Może jak to pominę na razie i wrócę poźniej to będzie lepiej.Pozdrawiam z 2019 lel