3. Instalacja i przegląd oprogramowania 3.1. Wymagania sprzętowe i programowe platformy.net Ponieważ od wprowadzenia technologii.net upłynęły już 3 lata, dlatego wzrosły wymagania w stosunku do wersji oprogramowania i sprzętu. Obecne wymagania sprzętowe i programowe są następujące: Windows NT4.0 (SP6), 2000 (SP4) lub XP, Internet Information Server (IIS), Internet Explorer 6.0, ADO 2.7 (ActiveX Data Objects), biblioteki.net Framework 1.1, program Web Matrix, dysk min. 10 GB, RAM min.128 MB, karta sieciowa i protokół TCP/IP. Platforma.NET w wersji 1.1 pozwala na tworzenie programów działających w środowisku Windows. Same programy można uruchamiać praktycznie na większości systemów Microsoftu, jednak jako platforma programistyczna wymaga ona do pracy systemu Windows 2000 lub Windows XP Professional. 3.2. Instalacja platformy.net Jedną z aplikacji działających na platformie.net jest darmowe narzędzie do tworzenia stron ASP.NET Web Matrix. Aby uruchomić ten program trzeba dokonać instalacji, składającej się z kilku etapów: aktualizacji przeglądarki internetowej (co najmniej do wersji 6 SP1), zalecana jest instalacja serwera IIS (Internet Information Server), aktualizacji systemu (np. przez Windows Update), instalacji bibliotek wykonawczych.net Framework (w języku angielskim), instalacji platformy programistycznej i dokumentacji.net SDK (Software Development Kit), instalacji polskiego pakietu językowego, instalacji najnowszych poprawek (Service Pack) dla platformy.net, 1
instalacji środowiska Web Matrix (co najmniej w wersji 0.6). Taka procedura instalacyjna pozwala uzyskać działające środowisko wraz z platformą. NET, gdzie komunikaty kompilatora (opisy błędów) są wyświetlane w języku polskim (również część okien z ustawieniami kontrolek będzie wyświetlana w języku polskim). Dodatkowa instalacja serwera IIS z płyty instalacyjnej Windows zalecana jest w przypadku, jeśli chce się przeglądać przykładowe aplikacje ASP.NET, dostarczone z dokumentacją.net Framework (Start Programy Microsoft.NET Framework SDK v1.1 Samples and QuickStart Tutorials). 3.3. Przegląd środowiska ASP.NET Web Matrix Narzędziem, które będzie wykorzystywane podczas kursu, jest darmowe środowisko Microsoft ASP.NET Web Matrix. Jest ono przeznaczone do tworzenia mniej skomplikowanych aplikacji ASP.NET, jak również edycji plików HTML, XML, styli kaskadowych CSS oraz prostych usług sieciowych. Ogólny wygląd aplikacji po uruchomieniu i otwarciu przykładowej strony jest następujący: Rysunek 5. Wygląd środowiska Web Matrix 2
Z lewej strony ekranu (1) widoczne jest okienko podręcznych narzędzi Toolbox, które zawiera cztery zakładki. Na zakładce Web Controls (najczęściej używanej) dostępne są programowalne komponenty ASP.NET, z których będzie budowany interfejs użytkownika. Zakładka HTML Elements (rzadziej używana) zawiera komponenty, będące odpowiednikami znaczników HTML, takich jak: <TEXTAREA>, <SELECT>, <INPUT TYPE="text"> czy <INPUT TYPE="submit">. Zakładka Custom Controls przydaje się, jeśli mamy zainstalowane dodatkowe kontrolki.net, pobrane np. z sieci. Czwarta zakładka My Snippets pozwala przyspieszyć pisanie kodu, przez możliwość umieszczenia tam dowolnych wycinków tekstu jest to taki podręczny schowek. Pojawiająca się podczas pisania kodu zakładka Code Wizards pozwala w prosty sposób dodać do strony metody realizujące operacje na bazie danych lub wysyłanie poczty e-mail. Jeśli na ekranie brakuje trochę miejsca, klawisz F2 pozwala na ukrywanie i ponowne wyświetlanie okienka Toolbox. W środkowej części ekranu (2) znajduje się obszar roboczy. Jeśli otwarte jest okno do edycji strony.aspx, to u dołu widoczne są cztery zakładki. Na zakładce Design tworzy się wizualny wygląd strony, przez przeciąganie komponentów i zmiany ich ustawień w oknie właściwości (4). Zakładka HTML pozwala oglądać i zmieniać część pliku.aspx, zwierającego wygenerowany kod strony, równoważny jej wizualnemu wyglądowi. Zakładka Code pozwala na edycję części strony zawierającej metody obsługujące zdarzenia. Zaś zakładka All pozwala na edycję całego pliku.aspx, co czasem jest bardzo przydatne (np. można w ten sposób dopisać dyrektywy dotyczące całej strony). Z prawej strony ekranu (3) znajduje się okienko projektu. Zakładka Workspace pozwala na przeglądanie plików z dysku. Zakładka Data służy do zarządzania połączeniami z bazami danych i umożliwia przeglądanie, tworzenie oraz edycję tabel w wybranej bazie. Zakładka Open Windows wyświetla listę wszystkich aktualnie otwartych okien, a ponieważ podczas pracy można otworzyć wiele dokumentów, każdy w osobnym oknie (często powiększonym na cały ekran), to taka podręczna lista okien jest bardzo pożyteczna. Prawą dolną część ekranu (4) zajmuje okienko właściwości. W połączeniu z wizualnym projektowaniem w oknach roboczych pozwala ono na pełną kontrolę nad wyglądem każdej strony. Główną zakładką jest tutaj Properties. Posiada ona od góry: listę komponentów na bieżącej stronie, listę znaczników, w których zawarty jest aktywny komponent oraz pasek przycisków. Pasek przycisków pozwala przełączać wyświetlanie właściwości w kolejności alfabetycznej lub w funkcjonalnych grupach. Następne dwa 3
przyciski przełączają między listą właściwości oraz listą zdarzeń komponentu (przyciski te są bardzo często wykorzystywane). Druga zakładka Classes pozwala przeglądać dokumentację komponentów.net w oknie środowiska Web Matrix oraz wyszukiwać potrzebne elementy w tej dokumentacji. Niezależnie od tego, po zainstalowaniu.net Framework SDK, dostępna jest także dużo bogatsza dokumentacja samej platformy. Taka dokumentacja jest kluczem do poznania możliwości i konkretnych szczegółów działania zarówno konkretnych komponentów, jak i całej platformy.net. Zakładka Community służy do wyszukiwania w sieci informacji dotyczących środowiska Web Matrix. Przydatne może być stwierdzenie, że okienka (3) i (4) można rozszerzać w poziomie, a także zmieniać ich proporcje w pionie. Warto ułatwiać sobie pracę, rozszerzając okienko właściwości, jest ono bowiem bardzo często używane, w przeciwieństwie do okienka projektu. Natomiast na głównym pasku narzędzi (5) jest jeden bardzo ważny przycisk: Start. Jego symbolem jest mały trójkącik, jak np. na odtwarzaczu CD. Przycisk ten uruchamia aplikację, a dokładniej aktualnie otwarty plik w ramach aplikacji. Rysunek 6. Start serwera ASP.NET Pierwszy start aplikacji jest poprzedzony uruchomieniem dołączonego do środowiska Web Matrix serwera ASP.NET. Po uruchomienia serwera kolejne wywołania stron przyciskiem Start wykonywane są w ramach tej samej aplikacji. Czasem też, żeby przełączyć się do innej aplikacji, trzeba zamknąć działający serwer lub uruchomić go na innym porcie. 4
4. Ćwiczenia do podstaw języka C# Przejdziemy teraz do części praktycznej. Zakładamy, że masz zainstalowaną platformę. NET wraz z SDK, jak również środowisko Web Matrix. Na początek spróbujemy wykonać ćwiczenia dotyczące podstaw języka C#. Bardziej rozbudowane strony (aplikacje) ASP.NET będą tworzone od drugiego modułu. Budowa konsoli tekstowej w aplikacji ASP.NET Celem tego ćwiczenia jest stworzenie interfejsu użytkownika, symulującego konsolę tekstową na stronie ASP.NET. Dodatkowym wymogiem jest dodanie konstrukcji programistycznych tak, aby można było, podobnie jak w rzeczywistej konsoli, używać takich wyrażeń, jak: Console.WriteLine() oraz Console.ReadLine(). Kod potrzebnych konstrukcji programistycznych (m.in. klasa HtmlConsole) będzie podany w dalszej części modułu. Strona będzie zawierać trzy główne części: wieloliniowe pole tekstowe do wyświetlania napisów, pole tekstowe do wprowadzania danych oraz zbiór przycisków uruchamiających różne podprogramy. Rysunek 7. Planowany wygląd aplikacji W celu stworzenia żądanej strony, wykonaj poniższe czynności: 1. Załóż na dysku twardym własny katalog na pliki dla tego ćwiczenia. 5
2. Otwórz program WebMatrix. 3. Stwórz nową stronę ASP.NET Page, upewnij się czy w polu Language jest wybrana opcja C#, a następnie zapisz stronę w swoim katalogu, w pliku o nazwie Konsola.aspx. 4. Wstaw do tej strony nagłówek: Konsola. 5. Wstaw kontrolkę TextBox i ustaw jej nazwę (właściwość ID) na TxtKonsola. 6. Za kontrolką wstaw tzw. złamanie wiersza, w następujący sposób: kliknij w kontrolkę, naciśnij na klawiaturze strzałkę w prawo, a następnie naciśnij Shift + Enter. 7. Wstaw drugą kontrolkę TextBox i ustaw jej nazwę (właściwość ID) na TxtLinia. 8. Wstaw tabelę, poleceniem HTML Insert Table. 9. Podaj następujące dane: Rows: 3 (ilość wierszy), Width: 622 (szerokość), Columns: 4 (ilość kolumn), Height: 62 (wysokość). 10. Do tabeli wstaw przyciski zgodnie z rysunkiem, nazywając je kolejno (czyli zmieniając właściwość ID) na: BtnCLS, BtnBW, BtnHello, BtnTypy, BtnKonwersje, BtnGwiazdki, BtnTablice, BtnOcena, BtnKwadraty, BtnTabliczka. 11. Zaznacz wszystkie przyciski, przytrzymując klawisz Shift i klikając w nie po kolei. Ustaw wszystkim przyciskom właściwość Width na 140. 12. Ustaw pozostałe właściwości kontrolek, zgodnie z poniższą tabelą: Kontrolka Nazwa kontrolki (ID) Nazwa właściwości Wartość właściwości TextBox TxtKonsola TextMode MultiLine ReadOnly True Width 630px Height 322px Columns 60 Rows 17 BackColor Black ForeColor White Font Courier New Font.Names Courier New, monospace BorderWidth 3px BorderStyle Inset TextBox TxtLinia Width 630px Columns 60 MaxLength 60 BackColor Black ForeColor White Font Courier New Font.Names Courier New, monospace BorderWidth 3px BorderStyle Inset Button BtnCLS Text CLS Width 140px Button BtnBW Text B / W Width 140px Button BtnHello Text Hello Width 140px 6
Button BtnTypy Text Typy Width 140px Button BtnKonwersje Text Konwersje Width 140px Button BtnGwiazdki Text Gwiazdki Width 140px Button BtnTablice Text Tablice Width 140px Button BtnOcena Text Ocena słownie Width 140px Button BtnKwadraty Text Kwadraty Width 140px Button BtnTabliczka Text Tabliczka mnożenia Width 140px 13. Zapisz dokonane zmiany i sprawdź, czy strona poprawnie wygląda w przeglądarce. W tym celu naciśnij mały przycisk Play na pasku narzędzi środowiska WebMatrix i zatwierdź uruchomienie serwera ASP.NET. Dodanie klasy realizującej konsolę Przyszła teraz pora na pierwsze wstawki programowe na stronie. Na początek jednak będzie to kod całkowicie gotowy i nietypowy zarazem. Będzie to bowiem klasa stworzona w języku C#, nazywająca się HtmlConsole, której zadaniem będzie emulowanie działania konsoli tekstowej (tak jak np. w wierszu poleceń). Poniższy kod należy wstawić do strony w sekcji <script>. W środowisku WebMatrix robi się to wyjątkowo prosto. Wystarczy przejść na zakładkę Code i wkleić w całości kod klasy. Kod 1. Klasa HtmlConsole //////////////////////////////////////////////////////////// class HtmlConsole { private TextBox OutputControl; private TextBox InputControl; public void Bind(TextBox output, TextBox input) { OutputControl = output; InputControl = input; if (OutputControl.Text == "") { Clear(); 7
public void Clear() { OutputControl.Text = "_"; InputControl.Text = ""; public void Write(string text) { string oldtext = OutputControl.Text.Substring(0, OutputControl.Text.Length - 1); OutputControl.Text = oldtext + text + "_"; public void WriteLine(string line) { Write(line + "\n"); public void WriteLine() { WriteLine(""); public void Write(string format, params object[] args) { Write(string.Format(format, args)); public void WriteLine(string format, params object[] args) { Write(format + "\n", args); public string ReadLine() { string line = InputControl.Text; if (line == "") return "0"; else return line; //////////////////////////////////////////////////////////// Kod ten wymaga wyjaśnienia. Klasa oparta jest na założeniu, że na stronie są dwie kontrolki typu TextBox. Jedna z nich służy za wyjście (OutputControl), druga za wejście (InputControl). Klasa posiada przeciążone metody Write() oraz WriteLine(), 8
przyjmujące różną ilość parametrów. Są tu też wersje przyjmujące dowolną ilość parametrów (z klauzulą params), tak jak rzeczywista konsola.net. Wewnątrz jednej takiej metody wykorzystana jest usługa klasy string, polegająca na formatowaniu wynikowego napisu, zgodnie z podanym formatem i parametrami. Oprócz tego jest też metoda ReadLine(), zwracająca tekst wpisany w kontrolce InputControl. Najbardziej uniwersalną metodą jest Write(string). Wszystkie pozostałe metody wyświetlające i tak w końcu przekazują do niej sterowanie, po ewentualnym sformatowaniu swoich parametrów. Metoda ta zapewnia całą strategię wyświetlania napisów, a dodatkowo jeszcze symulowany kursor (w postaci podkreślnika) na końcu wyświetlanego tekstu. Dodatkowe metody Bind() (powiązanie z kontrolkami wejścia i wyjścia) oraz Clear() (czyszczenie) organizują pracę konsoli. Dzięki takiej budowie uzyskany interfejs programowy jest praktycznie zgodny ze zwykłą konsolą tekstową.net. Pozwala to nawet, po drobnych rozszerzeniach, na skompilowanie wszystkich przykładów (oczywiście bez używania Bind oraz Clear) w program typu EXE, działający w trybie tekstowym. Pokazuje to ogromną elastyczność platformy ASP.NET. Obiekt konsoli i powiązanie go z kontrolkami Powyższy kod określa zasadę działania konsoli na stronie ASP.NET. Jednak nadal nie mamy możliwości jej obsługi. Dlaczego? To proste. Kod klasy jest przepisem na jej budowę (tak jak np. projekt samochodu). Żeby jednak zrobić coś pożytecznego, trzeba najpierw stworzyć odpowiedni obiekt (czyli wyprodukować samochód). Zdefiniujemy teraz zmienną Console, którą będziemy się posługiwać w celu wyświetlania i odczytywania tekstu. W tym celu pod kodem klasy HtmlConsole dodaj następujący kod: Kod 2. Zmienna Console //////////////////////////////////////////////////////////// HtmlConsole Console = new HtmlConsole(); //////////////////////////////////////////////////////////// Taka deklaracja zmiennej zapewni, że przy każdym ładowaniu strony zostanie stworzony nowy obiekt konsoli. Jednakże taka konsola nadal nie będzie poprawnie działać, ponieważ nie będzie wiedzieć, które kontrolki ma obsługiwać jako swoje wejście i wyjście. Dlatego trzeba dodać jeszcze kod wiążący konsolę z kontrolkami. Należy umieścić go w obsłudze zdarzenia Load strony. Ale jak? 9
Znowu całkiem prosto. Należy powrócić na zakładkę Design i w liście rozwijalnej komponentów odnaleźć element Page. Jest to obiekt reprezentujący stronę w pamięci serwera. Następnie należy przełączyć listę właściwości na listę zdarzeń (mały piorunek ) i zaznaczyć zdarzenie o nazwie Load. Można teraz otworzyć listę dostępnych procedur obsługi zdarzeń, żeby wybrać jedną, która będzie wywoływana na początku ładowania strony. Ponieważ jeszcze nie ma żadnych procedur obsługi zdarzeń, trzeba utworzyć nową. Robi się to przez dwukrotne kliknięcie po prawej stronie nazwy zdarzenia. W treść powstałej w ten sposób procedury Page_Load wstaw kod wiążący konsolę z kontrolkami, jak poniżej: Kod 3. Obsługa ładowania strony void Page_Load(object sender, EventArgs e) { Console.Bind(TxtKonsola, TxtLinia); Zauważ, że jeśli teraz uruchomisz stronę (ten sam przycisk Play), to w konsoli będzie wyświetlany znak podkreślenia, co oznacza, że konsola jest pusta. Jest to poprawny efekt, świadczący o wywołaniu metody Bind(), która w swojej treści wywołuje Clear(). Skoro tę część mamy już za sobą, pora na zajęcie się pierwszymi przyciskami. Dynamiczna zmiana właściwości kontrolek Działanie przycisku CLS jest wyjątkowo proste jego zadaniem jest wyczyszczenie konsoli. W tym celu na zakładce Design wystarczy raz kliknąć w ten przycisk, aby go zaznaczyć. Następnie należy przełączyć listę właściwości na listę zdarzeń i zaznaczyć zdarzenie Click. Podobnie, dwukrotne kliknięcie w to zdarzenie stworzy procedurę obsługi, którą wystarczy wypełnić wywołaniem metody Clear() konsoli: Kod 4. Czyszczenie konsoli void BtnCLS_Click(object sender, EventArgs e) { Console.Clear(); Przycisk B/W jest już jednak ciekawszy. Ma on za zadanie przełączać na zmianę kolory konsoli (białe na czarnym, czarne na białym). Podobnie, na zakładce Design trzeba wskazać ten przycisk i utworzyć procedurę obsługi zdarzenia Click. Poniższy kod 10
sprawdza, jaki jest aktualny kolor tła konsoli i zależnie od niego zmienia programowo właściwości dotyczące ustawień kolorów w kontrolkach TxtKonsola i TxtLinia: Kod 5. Zmiana kolorów konsoli void BtnBW_Click(object sender, EventArgs e) { if (TxtKonsola.BackColor == Color.Black) { // Czarne litery, białe tło TxtKonsola.ForeColor = TxtLinia.ForeColor = Color.Black; TxtKonsola.BackColor = TxtLinia.BackColor = Color.White; else { // Białe litery, czarne tło TxtKonsola.ForeColor = TxtLinia.ForeColor = Color.White; TxtKonsola.BackColor = TxtLinia.BackColor = Color.Black; Pojawia się jednak pewien problem strona nie chce się uruchomić, ponieważ pojawia się komunikat o błędzie (warto go zobaczyć!). Dotyczy on nieznanego symbolu Color, który m.in. definiuje wartości RGB różnych kolorów. Jednak skoro symbol ten jest dostępny na platformie.net, to dlaczego jest nieznany? Otóż symbol ten jest nieznany z pewnej subtelnej, ale istotnej przyczyny jest on zawarty w innej przestrzeni nazw. Przestrzeń nazw (inaczej obszar nazw, ang. namespace) służy do organizowania nazw symboli (takich jak np. Color) w odrębne, niebędące w konflikcie całości. Ma to zapobiegać konfliktom nazw, ponieważ przestrzeń nazw staje się częścią pełnej klasyfikowanej nazwy symbolu (pozwala to rozróżnić hipotetyczną listę jednokierunkową List od kontrolki listy List oraz obiektu List, zawierającego treść wysyłanego listu e-mail). Jak więc dowiedzieć się, w jakiej przestrzeni nazw rezyduje symbol Color? Należy skorzystać z przeglądarki klas, która jest dostępna po przełączeniu listy właściwości na listę klas (zakładka Classes). W polu Enter Name to Search wpisz Color i naciśnij Enter. Szukany symbol pojawi się tym razem jako pierwszy na liście, ponieważ czasem trzeba znaleźć go w pozostałych wynikach. Dwukrotne kliknięcie w znaleziony symbol wyświetli okno z opisem. Warto od razu zauważyć, że bardzo pożytecznym składnikiem tego okna jest opcja Show Non-Public Members. Pozwala ona na wyświetlenie na liście składników składowych odziedziczonych z klas rodzicielskich, czyli daje możliwość sprawdzenia wszystkich dostępnych elementów. 11
W naszym przypadku okazuje się, że pełną kwalifikowaną nazwą poszukiwanego symbolu jest System.Drawing.Color. Mamy teraz dwa wyjścia: albo poprawić kod obsługujący zmianę koloru, dopisując System.Drawing. przed każdym symbolem Color, albo wskazać stronie, aby, poszukując definicji symboli, korzystała dodatkowo z przestrzeni nazw System.Drawing. To drugie rozwiązanie jest oczywiście szybsze. Potrzebna będzie w takim razie dyrektywa Import. Aby móc ją dodać, należy najpierw przełączyć się na zakładkę All. Pod dyrektywą Page (czyli w drugiej linii pliku) wystarczy dopisać poniższą zaznaczoną linię: Kod 6. Importowanie przestrzeni nazw <%@ Page Language="C#" %> <%@ Import Namespace="System.Drawing" %> Teraz strona powinna ponownie działać i to całkiem dobrze pozwalając na szybką zmianę kolorów konsoli. Uwagi praktyczne Pracując ze środowiskiem WebMatrix warto pamiętać, że dwukrotne kliknięcie w kontrolkę jest prostym sposobem na stworzenie metody obsługi domyślnego zdarzenia. Typ takiego zdarzenia zależy od kontrolki, np. dla przycisku będzie to zdarzenie Click. Powoduje to automatyczne generowanie kodu i połączenie kontrolki ze stworzoną w ten sposób metodą. Jest to więc całkiem pożyteczna funkcja, ale trzeba uważać, aby jej nie nadużywać. W przeciwnym przypadku trzeba będzie skasować niepotrzebną metodę z zakładki Code oraz usunąć powiązanie kontrolki z metodą na zakładce ze zdarzeniami. Bardzo ciekawymi i przydatnymi funkcjami jest także śledzenie strony lub jej debugowanie. Śledzenie (ang. tracing) pozwala na podgląd w oknie przeglądarki szczegółów związanych z przetwarzaniem strony na serwerze. Debugowanie zaś jest czasem konieczne, żeby dowiedzieć się szczegółów dotyczących błędu występującego na stronie. Żeby włączyć takie funkcje trzeba w rozwijalnej liście komponentów odnaleźć element Document lub przełączyć się na zakładkę Code. Spowoduje to wyświetlenie właściwości strony, gdzie można ustawić wartość True dla właściwości Trace (aby włączyć śledzenie) oraz Debug (aby włączyć debugowanie). Należy przy tym pamiętać, że są to informacje pomocnicze dla twórcy strony i w środowisku produkcyjnym należy funkcje takie wyłączać. Rozbudowa strony 12
Przejdziemy teraz do właściwych ćwiczeń. Ich ogólna zasada działania jest prosta użytkownik ma wciskać różne przyciski. Jeśli coś ma zostać wyświetlone, będzie używana metoda Console.Write() lub Console.WriteLine(), a żądany tekst będzie pojawiał się w kontrolce TxtKonsola. Jeśli trzeba będzie coś odczytać, odpowiedni napis będzie pobierany metodą Console.ReadLine() z kontrolki TxtLinia. W tym celu do kolejnych przycisków dodasz obsługę zdarzenia Click i wpiszesz kod realizujący działania. Zauważ, że obsługa każdego przycisku będzie kończyć się wywołaniem metody Koniec(). Pozwoli ona zorientować się, gdzie skończył się tekst wypisany po naciśnięciu każdego przycisku. Należy więc dopisać taką metodę: Kod 7. Metoda Koniec() void Koniec() { Console.WriteLine("==KONIEC=="); Ćwiczenie 1 Hello Na pierwszy ogień pójdzie klasyczne Hello world. Dla urozmaicenia, tekst będzie wyświetlany w pętli for. Poniższy kod umieść w procedurze obsługi zdarzenia Click przycisku Hello : Kod 8. Wyświetlanie powitania żądaną liczbę razy void BtnHello_Click(object sender, EventArgs e) { for (int i = 1; i < 10; i++) { Console.WriteLine(i.ToString() + ") hello"); Koniec(); Zauważ, jak liczona jest ilość powtórzeń pętli. Możesz zmieniać granice powtarzania (wartość początkową oraz końcową i), jak również warunek powtarzania czy sposób zmiany wartości zmiennej i. Zauważ ponadto, jak aktualna wartość i jest zamieniana na napis (i.tostring()) przed wyświetleniem. Ćwiczenie 2 Typy danych Jest to ćwiczenie z dość długim kodem źródłowym, ale pozwalające zorientować się praktycznie w kilku aspektach platformy.net. Pierwszą i najciekawszą rzeczą jest stworzenie metody ZbadajTyp(), która umożliwia zbadanie zmiennej dowolnego typu bezpośredniego. Wymaga ona dwóch parametrów: zmiennej do zbadania oraz nazwy 13
badanego typu. Wykorzystane jest tutaj tzw. opakowywanie (ang. boxing), pozwalające podać w miejsce parametru typu object dowolną wartość, np. zmienną wybranego typu. Kod 9. Dostępne typy danych na platformie.net void ZbadajTyp(object o, string nazwa) { Console.WriteLine("Typ " + nazwa + "\t(np. wartość" + o +") to "+ o.gettype().namespace +"." + o.gettype().name); void BtnTypy_Click(object sender, EventArgs e) { // Typy całkowitoliczbowe, ze znakiem (czyli dodatnie i ujemne) sbyte dana_sbyte = -5; short dana_short = 25000; int dana_int = -20041107; long dana_long = 5000000000L; // Typy całkowitoliczbowe, bez znaku (tylko dodatnie i zero) byte dana_byte = 5; ushort dana_ushort = 25000; uint dana_uint = 40082214U; ulong dana_ulong = 9000000000UL; // Typy zmiennoprzecinkowe pojedynczej i podwójnej precyzji float dana_float = 1.5e9F; double dana_double = -3.2e-49; // Precyzyjny typ dziesiętny z 28 znaczącymi cyframi decimal dana_decimal = 1.239383M; // Typ znakowy char dana_char = 'x'; // Typ napisowy string dana_string = "napis"; // Typ logiczny (tylko dwie wartości: true i false) bool dana_bool = true; // Ostateczny typ bazowy wszystkich typów object dana_object = new object(); ZbadajTyp( dana_sbyte, "sbyte" ); ZbadajTyp( dana_short, "short" ); ZbadajTyp( dana_int, "int" ); ZbadajTyp( dana_long, "long" ); ZbadajTyp( dana_byte, "byte" ); ZbadajTyp( dana_ushort, "ushort" ); 14
ZbadajTyp( dana_uint, "uint" ); ZbadajTyp( dana_ulong, "ulong" ); ZbadajTyp( dana_float, "float" ); ZbadajTyp( dana_double, "double" ); ZbadajTyp( dana_decimal, "decimal" ); ZbadajTyp( dana_char, "char" ); ZbadajTyp( dana_string, "string" ); ZbadajTyp( dana_bool, "bool" ); ZbadajTyp( dana_object, "object" ); Koniec(); Metoda ZbadajTyp() dla podanej wartości odczytuje dwie informacje: nazwę przestrzeni nazw oraz nazwę typu danych. Okazuje się, że wszystkie podstawowe typy danych języka C# są synonimami typów danych platformy.net. Takie ujednolicenie typów danych jest jednym z elementów, dzięki którym platforma ta wspiera wiele języków programowania. Ćwiczenie 3 Konwersje Jest to kolejne ćwiczenie z dość długim kodem źródłowym. Ma ono na celu pokazanie możliwych konwersji między typami danych. Używane są tu m.in. konwersje między liczbami a napisami (i na odwrót), promocje całkowite i zmiennoprzecinkowe oraz konwersje zawężające (obcięcia). Kod 10. Konwersje między różnymi typami danych void BtnKonwersje_Click(object sender, EventArgs e) { // (1) Konwersja: liczba -> napis int liczba = 50; Console.WriteLine("Konwersja automatyczna:" + liczba); Console.Write("Konwersja jawna:"); Console.WriteLine(liczba.ToString()); // Odczyt liczby podanej przez użytkownika string podana = Console.ReadLine(); // (2) Konwersja: napis -> liczba 15
liczba = Convert.ToInt32(podana); // Warunki logiczne przy konwersjach typów if (liczba == 0) { Console.WriteLine( "Wpisz liczbę, żeby wykonać więcej konwersji..."); // Wcześniejsze zakończenie i wyjscie z metody Koniec(); return; // (3) Promocja całkowita: sbyte -> short, sbyte -> int // short -> int, int -> long long liczbalong = liczba; Console.WriteLine("Podana liczba:" + liczbalong); // Zbliżamy się do granicy zakresu liczb typu int liczbalong += 2147480000; // maks. liczba Int32 to 2147483647 Console.WriteLine("Zwiększona liczba long:" + liczbalong); // (4) Jawna konwersja zawężająca liczba = (int) liczbalong; Console.WriteLine("Odzyskana liczba int:" + liczba); if ((liczba < 0) (liczba!= liczbalong)) { Console.WriteLine("Wystąpił błąd konwersji!!!"); // Konwersja: napis -> liczba liczba = Convert.ToInt32(podana); Console.WriteLine("Jeszcze raz - podana liczba:" + liczba); // (5) Promocja zmiennoprzecinkowa Console.WriteLine("Liczba po pomnożeniu przez 2.137:" + liczba * 2.137); // Ten sam wynik co przed chwilą double liczbadouble = liczba * 2.137; // (6) Obcięcie części ułamkowej liczba = (int) liczbadouble; Console.WriteLine("Liczba po konwersji do int:" + liczba); 16
Koniec(); W przypadku (1) konwersja liczby na napis może odbywać się jawnie przez wywołanie dla liczby metody ToString(), która jest dostępna dla każdej zmiennej dowolnego typu. Alternatywą jest wykorzystanie konwersji automatycznej, kiedy wiadomo już, że potrzebny jest napis, a liczba jest tylko dołączana operatorem +. Metoda ToString() jest wtedy wywoływana automatycznie. Zauważ, że w takim przypadku liczba nigdy nie może być pierwsza może stać co najwyżej za innym napisem. W przypadku (2) konwersja napisów na liczby (int, short, double itp.) odbywa się z użyciem klasy Convert (konwertuj). Klasa ta posiada duży zbiór metod do konwersji między różnymi typami danych. Najczęściej jednak stosuje się metody: ToInt32(), ToInt16(), ToSByte(), ToDouble(), ToSingle(), ToUInt32(), ToUInt16(), ToByte(), ToBoolean(), ToInt64(), ToUInt64(). Każda z tych metod występuje w kilku wersjach, co daje możliwość łatwiejszego wykonywania konwersji. Przypadek (3) pokazuje tzw. promocje całkowite. Można tu zauważyć prostą zasadę jest to konwersja automatyczna, pozwalająca bez utraty dokładności zamienić wartości mniejszego typu na wartości większego typu. Przykładowo, wartość 120 typu sbyte (mającego zakres od 128 do 127) można bezstratnie zamienić na wartość 120 typu short (o zakresie od 32 768 do 32 767). W przypadku odwrotnym (4) można zamienić wartości większego typu na wartości mniejszego typu, obcinając najbardziej znaczące bity. Jednak trzeba się tu liczyć z możliwością powstania błędów wynikających z takiej konwersji. Na przykład wartość 3000 typu ushort (typ całkowity, 16-bitowy, bez znaku) po obcięciu do typu byte (typ całkowity, 8-bitowy, bez znaku) zamienia się na wartość 184. Wynika to z postaci liczb typu ushort: 3000 = (11 * 256) + 184. Tylko mniej znaczący bajt jest zostawiany i stąd niedokładność. Jeszcze ciekawsze problemy stwarza konwersja na mniejsze typy ze znakiem, ponieważ najstarszy bit takich liczb ma wartość ujemną. Dla przykładu: liczba long (64-bitowa, ze znakiem) o wartości: 2800300560 = 00000000A6E93210 (16) = (166 * 2 24 ) + (233 * 2 16 ) + (50 * 2 8 ) + 16, po obcięciu do liczby typu int (32-bitowa, ze znakiem) zamienia się na: = ( 1 * 2 31 ) + ((166 128) * 2 24 ) + (233 * 2 16 ) + (50 * 2 8 ) + 16 = A6E93210 (16) = 1494666736. Wszystko to wynika z reprezentacji liczb w pamięci w postaci binarnej. 17
Warto więc pamiętać o zawsze działających promocjach całkowitych i nie zawsze działających obcięciach zakresów. W przypadku (5) mamy podobnie zachowujące się promocje zmiennoprzecinkowe. Konwersja liczby float na double udaje zawsze się, konwersja w drugą stronę (obcięcie) może spowodować niedokładność. Przypadek (6) jest ciekawy i dość często wykorzystywany. Konwersja liczby zmiennoprzecinkowej na liczbę całkowitą polega na odrzuceniu części ułamkowej. Nawet, jeśli jest to np. liczba 13,999999, po konwersji pozostanie tylko całkowita wartość 13. Należy pamiętać, aby wynikowa liczba mieściła się w zakresie docelowego typu, ponieważ inaczej mogą powstać niedokładności. Zaokrąglanie zgodne z regułami matematycznymi zapewnia metoda Math.Round(). Ćwiczenie 4 Gwiazdki Jest to ćwiczenie proste, a jednocześnie ciekawe. Jego celem jest wyświetlenie trójkątnego wzoru składającego się z gwiazdek tak aby każdy wiersz był dłuższy (lub krótszy) od kolejnego o jedną gwiazdkę. Podstawowym założeniem ćwiczenia jest możliwość użycia tylko trzech instrukcji wyświetlających: Console.Write("*"), Console.Write(" ") oraz Console.WriteLine(). Kod 11. Wyświetlenie trójkątnego wzoru składającego się z gwiazdek void BtnGwiazdki_Click(object sender, EventArgs e) { int ile = Convert.ToInt32(Console.ReadLine()); if (ile < 1) { Console.WriteLine("Podaj liczbę wierszy!"); for (int y = 1; y <= ile; y++) { for (int x = 1; x <= y; x++) { Console.Write("*"); Console.WriteLine(); Koniec(); Na początku pobierana jest liczba określająca żądaną liczbę wierszy wzoru. Jak można się domyślić, możliwe są cztery układy wzorów. Tutaj mamy do czynienia z najprostszą wersją: w każdym wierszu wyświetlanych jest tyle gwiazdek, jaki jest numer wiersza. 18
Cała tajemnica tkwi w umiejętnym zagnieżdżeniu pętli w pętli oraz w rozważnym doborze granic indeksowania w każdej z pętli. Ćwiczenie 5 Tablice Tablice służą do gromadzenia danych tego samego typu, dostępnych przez numer elementu, czyli tzw. indeks. Język C# ma kilka zasad dotyczących tablic. W poniższym przykładzie przedstawione są różne sposoby tworzenia, wypełniania i używania tablic. Co prawda wykorzystane są tu tylko tablice jednowymiarowe, ale główne mechanizmy są takie same także dla tablic wielowymiarowych. Kod 12. Różne sposoby tworzenia i wykorzystania tablic void PokazTablice(int[] tab, string nazwa) { Console.WriteLine("::" + nazwa + "::"); for (int i = 0; i < tab.length; i++) { //Console.WriteLine("{0[{1] = {2", nazwa, i, tab[i]); Console.Write("[{0]={1\t", i, tab[i]); Console.WriteLine(); void BtnTablice_Click(object sender, EventArgs e) { // Tablica ze znanymi elementami. int[] tab1 = { 20, 30, 15, 20 ; PokazTablice(tab1, "tab1"); // Kopia tablicy? Raczej jej "duch". int[] tab2 = tab1; tab1[3] = 99; PokazTablice(tab2, "tab2"); // Dynamicznie tworzona tablica ze znanymi elementami. int[] tab3 = new int[] { 5, 10, 15, 20, 25 ; 19
PokazTablice(tab3, "tab3"); // Dynamicznie tworzona tablica z losowymi elementami. int[] tab4 = new int[7]; Random rnd = new Random(); for (int i = 0; i < tab4.length; i++) { tab4[i] = rnd.next(90) + 10; PokazTablice(tab4, "tab4"); Koniec(); W powyższym przykładzie stworzona jest metoda PokazTablice(). Jej celem jest przejrzyste wyświetlenie zawartości tablicy liczb całkowitych. Może ona obsłużyć dowolną tablicę, przez fakt skorzystania z dwóch fundamentalnych cech tablic. Po pierwsze, każda tablica jest obiektem, co oznacza, że w celu sięgnięcia do jej zawartości trzeba posługiwać się uchwytem. Przekazywanie oraz kopiowanie uchwytu nie zmienia zawartości tablicy ani nie powoduje tworzenia kopii tablicy. Po drugie, każda tablica posiada pole o nazwie Length. Dzięki odczytowi tego pola można dowiedzieć się, jaki jest rozmiar wybranej tablicy (nie można go zmieniać każda tablica ma stały rozmiar, określony przy jej tworzeniu). Dodatkowo, w celu wyświetlenia nazwy tablicy opisywana metoda posiada parametr nazwa ponieważ samej nazwy zmiennej nie można odczytać dynamicznie. Warto zauważyć, w jaki sposób metoda ta wyświetla kolejne elementy tablicy. W tym celu wywoływana jest metoda Write() z podaniem formatu zawierającego numery parametrów (liczone od zera) w nawiasach klamrowych. Za napisem formatu podawane są wartości parametrów, które trafią w żądane miejsca, zgodnie z określonym formatem. Ponadto w programie pokazane są różne sposoby tworzenia tablic. Tablica tab1 ma cztery elementy, podane przy jej deklaracji. Powielenie uchwytu do tej tablicy i zapisanie go w zmiennej tab2 pokazuje efekt pierwszej reguły dotyczącej tablic tab1 i tab2 są od tej pory uchwytami do tej samej tablicy. Zmiana zawartości przez uchwyt tab1 pozwala odczytać te zmiany przez uchwyt tab2. Innym, równoważnym sposobem tworzenia tablicy jest wykorzystanie wyrażenia new int [], za którym podane są wartości do umieszczenia w tablicy. Rozmiar tablicy jest wtedy równy liczbie podanych elementów. Można również zrobić inaczej, tak jak jest to 20
pokazane przy tworzeniu tablicy tab4 najpierw należy stworzyć tablicę żądanego rozmiaru, później wypełnić ją wartościami (tu: losowymi), a następnie wykorzystać. Przy okazji warto zauważyć, w jaki sposób tworzony jest obiekt Random i jak losowane są kolejne liczby całkowite z zakresu 10 99. Ćwiczenie 6 Ocena słowna Celem ćwiczenia jest pobranie od użytkownika oceny i wyświetlenie jej interpretacji słownej. Rozpoznawane będą tylko oceny 2, 3, 4 i 5. Dla pozostałych przypadków będzie wyświetlany komunikat Nie rozumiem. Kod 13. Wyświetlanie słownej interpretacji oceny z użyciem instrukcji switch void BtnOcena_Click(object sender, EventArgs e) { int ocena = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("Liczba: {0", ocena); Console.Write("Słownie:"); switch (ocena) { case 5: Console.Write("bardzo"); goto case 4; case 4: Console.WriteLine("dobry"); break; case 3: Console.WriteLine("dostateczny"); break; case 2: Console.WriteLine("niedostateczny"); break; default: Console.WriteLine("nie rozumiem"); break; Koniec(); Ćwiczenie to jest przykładem wykorzystania instrukcji switch. Otóż w języku C# każdy przypadek instrukcji switch musi kończyć się instrukcją break, goto case lub goto default. W ten sposób, czytając kod, zawsze wiadomo, jaka będzie kolejność wykonania programu dla każdego przypadku. 21
Ćwiczenie 7 Kwadraty Ćwiczenie to ma na celu praktyczne wykorzystanie dynamicznego tworzenia tablic. Kod 14. Wyświetlanie tablicy kwadratów liczb void BtnKwadraty_Click(object sender, EventArgs e) { int liczba = Convert.ToInt32(Console.ReadLine()); if (liczba < 1) { Console.WriteLine("Podaj największą liczbę, " + "której kwadrat chcesz zobaczyć."); else { int[] kwadraty = new int[liczba]; for (int i = 0; i < liczba; i++) { kwadraty[i] = (i+1) * (i+1); Console.WriteLine("Podana liczba:" + liczba.tostring()); for (int i = 0, l = 1; i < kwadraty.length; i++, l++) { Console.WriteLine("Kwadrat liczby {0 = {1", l, kwadraty[i]); Koniec(); Najpierw sprawdzane jest to, czy użytkownik podał liczbę dodatnią. Jeśli tak, tworzona jest tablica kwadraty o wskazanym rozmiarze (new int[liczba]), jest ona następnie wypełniana kolejnymi kwadratami liczb. Warto podkreślić, że skoro wszystkie tablice indeksowane są od zera, to przy zapisie wartości do tablicy trzeba wykorzystać kwadrat aktualnej wartości licznika pętli i powiększonego o jeden. Tablica jest następnie wyświetlana z wykorzystaniem informacji o jej rozmiarze, odczytanej z pola Length. Ćwiczenie 8 Tabliczka mnożenia Ćwiczenie to prowadzi do wyświetlenia prostej tabliczki mnożenia, bez dodatkowych nagłówków określających mnożone liczby. Wyświetlane są jedynie wyniki mnożenia. Kod 15. Wyświetlanie tabliczki mnożenia z własnym formatowaniem liczb void BtnTabliczka_Click(object sender, EventArgs e) { const int ROZMIAR = 5; int y = 1; while (y <= ROZMIAR) { int x = 1; 22
while (x <= ROZMIAR) { Console.Write("{0,6:F2", (double) x * y); x++; Console.WriteLine(); y++; Koniec(); Tym razem dla odmiany zastosowane są zagnieżdżone pętle while. Warto zauważyć, że rozmiar tablicy jest określony przez stałą ROZMIAR. Jej wartość określona jest w kodzie jednokrotnie i jak w przypadku każdej stałej nie może ulegać zmianie (chyba, że zostanie poprawiony sam kod). Charakterystyczne jest tutaj to, że wyniki mnożenia są wyświetlane jako liczby zmiennoprzecinkowe, przez wymuszoną rzutowaniem promocję zmiennoprzecinkową. Dodatkowo zastosowany jest tutaj własny sposób formatowania liczb, którego opis można znaleźć w dokumentacji platformy.net pod hasłami: formatting strings, composite formatting oraz standard numeric format strings. Podany tu schemat formatu "{0,6:F2" należy rozumieć następująco: zerowy parametr (czyli wynik mnożenia) wyświetl w polu o długości 6 znaków, dosuwając wyświetlane znaki do prawej strony (do lewej, gdyby było 6). Wyświetlona będzie liczba zmiennoprzecinkowa ("F") z dwoma miejscami po przecinku. 23