Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00051 008660 10442594 na godz. na dobę w sumie
C#. Receptury. Wydanie II - książka
C#. Receptury. Wydanie II - książka
Autor: , Liczba stron: 1064
Wydawca: Helion Język publikacji: polski
ISBN: 83-246-0476-6 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> c# - programowanie
Porównaj ceny (książka, ebook, audiobook).

Zbiór gotowych rozwiązań dla programistów C# i .NET 2.0

C# to jeden z języków programowania przeznaczonych dla platformy .NET. Został tak skonstruowany, że programiści, którzy wcześniej korzystali z języków Java bądź C++, bez problemu opanują zasady programowania w C#. Według twórcy C# -- firmy Microsoft -- język ten jest nowatorskim narzędziem programowania na platformie .NET, niesprawiającym kłopotów programistom znającym inne języki, a jednocześnie zapewniającym większą kontrolę nad działającym kodem w fazie wykonywania. W nowej wersji platformy .NET, oznaczonej numerem 2.0, wprowadzono również nową wersję języka C#, oferującą dodatkowe możliwości.

Książka 'C#. Receptury. Wydanie II' to zbiór ponad 300 porad, które programistom C# pomogą rozwiązać zadania programistyczne, z jakim spotykają się w codziennej pracy. Przedstawiono w niej metody przetwarzania danych tekstowych i liczbowych, zasady korzystania z wyrażeń regularnych oraz typów generycznych -- nowości w C# 2.0. Omówiono sposoby przetwarzania plików XML, obsługę wyjątków oraz reguły tworzenia aplikacji sieciowych ASP.NET i aplikacji dla systemu Windows.

Przyspiesz tempo swojej pracy -- korzystaj z gotowych rozwiązań.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

IDZ DO IDZ DO PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ SPIS TREœCI SPIS TREœCI KATALOG KSI¥¯EK KATALOG KSI¥¯EK KATALOG ONLINE KATALOG ONLINE ZAMÓW DRUKOWANY KATALOG ZAMÓW DRUKOWANY KATALOG TWÓJ KOSZYK TWÓJ KOSZYK DODAJ DO KOSZYKA DODAJ DO KOSZYKA CENNIK I INFORMACJE CENNIK I INFORMACJE ZAMÓW INFORMACJE ZAMÓW INFORMACJE O NOWOœCIACH O NOWOœCIACH ZAMÓW CENNIK ZAMÓW CENNIK CZYTELNIA CZYTELNIA FRAGMENTY KSI¥¯EK ONLINE FRAGMENTY KSI¥¯EK ONLINE Wydawnictwo Helion ul. Koœciuszki 1c 44-100 Gliwice tel. 032 230 98 63 e-mail: helion@helion.pl C#. Receptury. Wydanie II Autorzy: Jay Hilyard, Stephen Teilhet T³umaczenie: Rados³aw Meryk (przedmowa, rozdz. 1 – 11), Daniel Kaczmarek (rozdz. 12 – 20) ISBN: 83-246-0476-6 Tytu³ orygina³u: C# Cookbook Format: B5, stron: 1064 Zbiór gotowych rozwi¹zañ dla programistów C# i .NET 2.0 (cid:129) Przetwarzanie liczb i tekstów (cid:129) Obs³uga b³êdów i wyj¹tków (cid:129) Aplikacje sieciowe C# to jeden z jêzyków programowania przeznaczonych dla platformy .NET. Zosta³ tak skonstruowany, ¿e programiœci, którzy wczeœniej korzystali z jêzyków Java b¹dŸ C++, bez problemu opanuj¹ zasady programowania w C#. Wed³ug twórcy C# — firmy Microsoft — jêzyk ten jest nowatorskim narzêdziem programowania na platformie .NET, niesprawiaj¹cym k³opotów programistom znaj¹cym inne jêzyki, a jednoczeœnie zapewniaj¹cym wiêksz¹ kontrolê nad dzia³aj¹cym kodem w fazie wykonywania. W nowej wersji platformy .NET, oznaczonej numerem 2.0, wprowadzono równie¿ now¹ wersjê jêzyka C#, oferuj¹c¹ dodatkowe mo¿liwoœci. Ksi¹¿ka „C#. Receptury. Wydanie II” to zbiór ponad 300 porad, które programistom C# pomog¹ rozwi¹zaæ zadania programistyczne, z jakim spotykaj¹ siê w codziennej pracy. Przedstawiono w niej metody przetwarzania danych tekstowych i liczbowych, zasady korzystania z wyra¿eñ regularnych oraz typów generycznych — nowoœci w C# 2.0. Omówiono sposoby przetwarzania plików XML, obs³ugê wyj¹tków oraz regu³y tworzenia aplikacji sieciowych ASP.NET i aplikacji dla systemu Windows. (cid:129) Operacje na liczbach (cid:129) Przetwarzanie ci¹gów znaków (cid:129) Typy generyczne (cid:129) Kolekcje i iteratory (cid:129) Dzienniki zdarzeñ (cid:129) Obs³uga zdarzeñ (cid:129) Korzystanie z wyra¿eñ regularnych (cid:129) Operacje na systemie plików (cid:129) Tworzenie aplikacji sieciowych (cid:129) Zabezpieczanie kodu Przyspiesz tempo swojej pracy — korzystaj z gotowych rozwi¹zañ Przedmowa ....................................................................................................................17 1. Liczby i typy wyliczeniowe .......................................................................................... 27 27 1.0. Wprowadzenie 1.1. Określanie przybliżonej równości pomiędzy wartością ułamkową a zmiennoprzecinkową 1.2. Konwersja stopni na radiany 1.3. Konwersja radianów na stopnie 1.4. Zastosowanie operatora negacji bitowej do danych różnych typów 1.5. Sprawdzenie, czy liczba jest parzysta czy nieparzysta 1.6. Uzyskanie bardziej znaczącego i mniej znaczącego słowa liczby 1.7. Konwersja liczby z innego systemu liczbowego na dziesiętny 1.8. Sprawdzenie, czy ciąg znaków reprezentuje prawidłową liczbę 1.9. Zaokrąglanie wartości zmiennoprzecinkowych 1.10. Wybór algorytmu zaokrąglania 1.11. Zamiana stopni Celsjusza na stopnie Fahrenheita 1.12. Zamiana stopni Fahrenheita na stopnie Celsjusza 1.13. Bezpieczna konwersja liczb do mniejszego rozmiaru 1.14. Wyznaczanie długości dowolnego z trzech boków trójkąta prostokątnego 1.15. Obliczanie kątów trójkąta prostokątnego 1.16. Wyświetlanie wartości typu wyliczeniowego w postaci tekstowej 1.17. Konwersja zwykłego tekstu na odpowiedniki w postaci wartości typu wyliczeniowego 1.18. Sprawdzanie poprawności wartości typu wyliczeniowego 1.19. Sprawdzanie poprawności typu wyliczeniowego z atrybutem Flags 1.20. Zastosowanie elementów typu wyliczeniowego w masce bitowej 1.21. Sprawdzanie, czy ustawiono jedną czy kilka flag w danych typu wyliczeniowego 1.22. Wyznaczanie części całkowitej zmiennej typu decimal lub double 28 30 31 31 33 34 35 37 37 38 39 40 40 43 44 45 47 48 50 52 55 58 5 2. Znaki i ciągi znaków ..................................................................................................... 59 59 59 62 63 65 67 71 2.0. Wprowadzenie 2.1. Określenie rodzaju znaku w zmiennej char 2.2. Sprawdzanie, czy znak znajduje się w określonym zakresie 2.3. Porównywanie znaków z rozróżnianiem wielkości liter i bez niego 2.4. Wyszukiwanie wszystkich wystąpień znaku w ciągu 2.5. Wyszukiwanie wszystkich wystąpień jednego ciągu znaków w innym 2.6. Implementacja prostego analizatora dzielącego tekst na słowa 2.7. Zarządzanie rozróżnianiem wielkich i małych liter podczas porównywania dwóch ciągów znaków 2.8. Porównywanie jednego ciągu znaków z początkiem lub końcem innego 2.9. Wstawianie tekstu do ciągu znaków 2.10. Usuwanie lub zastępowanie znaków w ciągu 2.11. Kodowanie danych binarnych w formacie Base64 2.12. Dekodowanie danych binarnych zakodowanych w formacie Base64 2.13. Konwersja obiektu String zwróconego w formacie Byte[] na postać String 2.14. Przekazywanie ciągu znaków do metody, która akceptuje wyłącznie dane typu byte[] 2.15. Konwersja ciągów znaków na dane innych typów 2.16. Formatowanie danych w ciągach znaków 2.17. Tworzenie ciągu znaków rozdzielanego separatorami 2.18. Wyodrębnianie elementów z tekstu rozdzielanego separatorami 2.19. Ustawienie maksymalnej liczby znaków dla obiektów klasy StringBuilder 2.20. Przetwarzanie w pętli wszystkich znaków w ciągu 2.21. Poprawa wydajności porównywania ciągów znaków 2.22. Poprawa wydajności aplikacji wykorzystujących klasę StringBuilder 2.23. Usuwanie znaków z początku i (lub) końca ciągu 2.24. Sprawdzanie, czy ciąg znaków jest pusty lub zawiera wartość null 2.25. Dołączanie wiersza 2.26. Kodowanie danych przekazywanych porcjami 73 74 75 76 78 79 80 82 83 86 89 90 91 92 94 97 100 101 101 102 3. Klasy i struktury .......................................................................................................... 109 109 111 113 118 122 127 132 136 3.0. Wprowadzenie 3.1. Tworzenie struktur działających jak unie 3.2. Wyprowadzanie wartości typu w postaci ciągu znaków 3.3. Konwersja znakowej reprezentacji obiektu na obiekt 3.4. Implementacja polimorfizmu za pomocą abstrakcyjnych klas bazowych 3.5. Zapewnienie możliwości sortowania danych zdefiniowanego typu 3.6. Zapewnienie możliwości wyszukiwania danych typu 3.7. Pośrednie przeciążanie operatorów +=, -=, /= i *= 6 | Spis treści 3.8. Pośrednie przeciążanie operatorów , || i ?: 3.9. Włączanie i wyłączanie bitów 3.10. Tworzenie bezbłędnych wyrażeń 3.11. Upraszczanie wyrażeń logicznych 3.12. Konwersja prostych typów danych w sposób niezależny od języka 3.13. Kiedy należy używać operatora cast, a kiedy as lub is? 3.14. Konwersja za pomocą operatora as 3.15. Sprawdzanie typu zmiennej za pomocą operatora is 3.16. Implementacja polimorfizmu za pomocą interfejsów 3.17. Wywoływanie tej samej metody dla wielu typów obiektowych 3.18. Implementacja wywoływanej zwrotnie metody powiadamiającej z wykorzystaniem interfejsów 3.19. Wykorzystanie wielu punktów wejścia w celu stworzenia kilku wersji aplikacji 3.20. Zapobieganie tworzeniu częściowo zainicjowanych obiektów 3.21. Zwracanie wielu elementów przez metodę 3.22. Analiza parametrów wiersza polecenia 3.23. Przystosowanie klasy do współpracy z obiektami COM 3.24. Inicjowanie stałej w fazie wykonywania programu 3.25. Pisanie kodu zgodnego z jak największą liczbą zarządzanych języków 3.26. Tworzenie klas, które można klonować 3.27. Zapewnienie niszczenia obiektu 3.28. Zwalnianie obiektu COM z poziomu zarządzanego kodu 3.29. Tworzenie pamięci podręcznej obiektów 3.30. Wycofywanie zmian wprowadzonych w obiektach 3.31. Zwalnianie niezarządzanych zasobów 3.32. Wyszukiwanie operacji pakowania i rozpakowania 139 141 145 147 150 156 157 159 162 165 167 175 176 178 181 188 192 195 196 199 202 203 212 218 224 4. Typy generyczne ......................................................................................................... 227 227 227 228 235 236 240 244 247 249 4.0. Wprowadzenie 4.1. Gdzie i kiedy korzystać z typów generycznych? 4.2. Podstawowe wiadomości o typach generycznych 4.3. Odczytywanie obiektu Type dla danych typu generycznego 4.4. Zastępowanie typu ArrayList jego generycznym odpowiednikiem 4.5. Zastąpienie obiektów Stack i Queue ich generycznymi odpowiednikami 4.6. Implementacja powiązanych list 4.7. Tworzenie typu wartości, który można zainicjować wartością null 4.8. Odwrócenie porządku posortowanej listy 4.9. Tworzenie kolekcji tylko do odczytu z wykorzystaniem typów generycznych 4.10. Zastąpienie typu Hashtable jego generycznym odpowiednikiem 271 273 Spis treści | 7 4.11. Korzystanie z pętli foreach dla generycznego typu Dictionary 4.12. Ograniczenia dla argumentów opisujących typy 4.13. Inicjowanie zmiennych generycznych na ich wartości domyślne 276 277 279 5. Kolekcje ....................................................................................................................... 281 281 283 284 286 288 289 294 297 300 302 304 5.0. Wprowadzenie 5.1. Zamiana miejscami dwóch elementów w tablicy 5.2. Szybkie odwracanie tablicy 5.3. Odwracanie tablic dwuwymiarowych 5.4. Odwracanie tablic postrzępionych 5.5. Bardziej uniwersalna klasa StackTrace 5.6. Określanie liczby wystąpień elementu na liście List T 5.7. Wyodrębnianie wszystkich egzemplarzy określonego elementu z listy List T 5.8. Wstawianie i usuwanie elementów z tablicy 5.9. Utrzymywanie listy List T w stanie posortowanym 5.10. Sortowanie indeksów i (lub) wartości obiektu klasy Dictionary 5.11. Tworzenie obiektu Dictionary z ograniczeniami dla wartości minimalnej i maksymalnej 307 5.12. Wyświetlanie danych z tablicy w postaci ciągu znaków rozdzielanych separatorami 310 311 5.13. Zapisywanie migawek list w tablicy 312 5.14. Utrzymywanie kolekcji pomiędzy sesjami aplikacji 5.15. Sprawdzanie wszystkich elementów tablicy Array bądź List T 314 5.16. Wykonywanie operacji dla każdego elementu danych typu Array bądź List T 315 5.17. Tworzenie obiektów tylko do odczytu typu Array lub List T 317 6. Iteratory i typy częściowe .......................................................................................... 319 319 6.0. Wprowadzenie 320 6.1. Implementacja zagnieżdżonych pętli foreach dla klasy 6.2. Tworzenie własnej obsługi pętli foreach 324 327 6.3. Tworzenie iteratorów dla typu generycznego 329 6.4. Tworzenie iteratora dla typu niegenerycznego 6.5. Tworzenie iteratorów z parametrami 331 333 6.6. Definiowanie wielu iteratorów dla jednego typu 336 6.7. Implementacja iteratorów jako operatorów przeciążonych 342 6.8. Wymuszone zatrzymywanie iteratora 6.9. Obsługa bloku finally w iteratorach 344 347 6.10. Organizacja implementacji interfejsów 6.11. Generowanie kodu spoza głównej ścieżki 351 8 | Spis treści 7. Obsługa wyjątków ..................................................................................................... 355 355 361 364 365 365 7.0. Wprowadzenie 7.1. Weryfikacja parametrów kluczowych 7.2. Gdzie należy przechwytywać i ponawiać zgłaszanie wyjątków? 7.3. Identyfikacja wyjątków i ich zastosowanie 7.4. Indywidualna obsługa wyjątków pochodnych 7.5. Jak zapewnić, aby wyjątki nie były tracone w przypadku wykorzystania bloków finally? 7.6. Obsługa wyjątków zgłaszanych przez metody wywoływane za pomocą odbić 7.7. Diagnozowanie problemów podczas ładowania kompilatów 7.8. Odwzorowania pomiędzy zarządzanymi wyjątkami a wartościami HRESULT 7.9. Obsługa wartości HRESULT definiowanych przez użytkownika 7.10. Przeciwdziałanie nieobsłużonym wyjątkom 7.11. Uzyskiwanie informacji o wyjątkach 7.12. Jak szybko dostać się do meritum problemu? 7.13. Tworzenie własnych typów opisu wyjątków 7.14. Odczytywanie obrazu stosu 7.15. Ustawienie pułapki w miejscu, gdzie może wystąpić wyjątek „pierwszej szansy” 7.16. Zapobieganie wyjątkowi TypeInitializationException 7.17. Obsługa wyjątków zgłaszanych przez delegaty asynchroniczne 7.18. Przekazywanie do wyjątków dodatkowych informacji za pośrednictwem pola Exception.Data 7.19. Prezentacja wyjątków w sposób niestandardowy za pomocą wizualizatorów 7.20. Postępowanie z nieobsłużonymi wyjątkami w aplikacjach WinForms 369 372 374 376 379 380 382 385 386 397 399 401 405 406 408 414 8. Diagnostyka .................................................................................................................417 417 418 421 424 428 430 432 438 439 443 8.0. Wprowadzenie 8.1. Zarządzanie wynikami diagnostycznymi we wdrożonych aplikacjach 8.2. Szczegółowe zarządzanie wynikami debugowania (śledzenia) 8.3. Tworzenie własnych klas przełączników 8.4. Warunkowa kompilacja bloków kodu 8.5. Jak sprawdzić, czy proces przestał odpowiadać? 8.6. Wykorzystanie dzienników zdarzeń w aplikacji 8.7. Modyfikacja maksymalnego rozmiaru niestandardowego dziennika zdarzeń 8.8. Wyszukiwanie zapisów w dzienniku zdarzeń 8.9. Obserwacja specyficznego zapisu w dzienniku zdarzeń 8.10. Wyszukiwanie wszystkich źródeł należących do określonego dziennika zdarzeń 8.11. Implementacja prostego licznika wydajności 8.12. Implementacja liczników wydajności, które wymagają liczników bazowych 444 446 449 Spis treści | 9 8.13. Włączanie i wyłączanie złożonego kodu śledzenia 8.14. Przechwytywanie standardowego wyniku procesu 8.15. Tworzenie niestandardowego wyjścia debugowania dla klas użytkownika 8.16. Odczytywanie ustawień bieżącej domeny AppDomain 8.17. Programowe podwyższanie priorytetu procesu 8.18. Analiza środowiska wykonawczego w celu diagnozowania problemów 452 455 457 459 462 463 9. Delegaty, zdarzenia i metody anonimowe ...............................................................465 465 9.0. Wprowadzenie 9.1. Zarządzanie czasem i miejscem uruchomienia delegatu w obrębie delegatu multicast 9.2. Odczytywanie zwracanych wyników każdego z delegatów wchodzących w skład delegatu multicast 9.3. Indywidualna obsługa wyjątków dla każdego z delegatów w obrębie delegatu multicast 9.4. Konwersja wywołania delegatu z synchronicznego na asynchroniczne 9.5. Opakowywanie zapieczętowanych klas w celu dodawania zdarzeń 9.6. Przekazywanie specjalizowanych parametrów do zdarzenia i ze zdarzenia 9.7. Zaawansowany mechanizm wyszukiwania interfejsów 9.8. Zaawansowany mechanizm wyszukiwania składowych 9.9. Obserwacja dodawania i modyfikowania elementów w tablicy Hashtable 9.10. Wykorzystanie „haków” do klawiszy Windows 9.11. Śledzenie operacji wykonywanych myszą i reagowanie na nie 9.12. Zastosowanie metod anonimowych 9.13. Lepsza konfiguracja metod obsługi zdarzeń 9.14. Wykorzystywanie różnych modyfikatorów parametrów w metodach anonimowych 9.15. Zastosowanie domknięć w języku C# 9.16. Wykonywanie wielu operacji na liście z wykorzystaniem funktorów 466 469 471 474 477 483 488 491 495 502 508 509 513 516 519 523 10. Wyrażenia regularne .................................................................................................. 527 527 528 531 533 535 536 539 542 543 545 10.0. Wprowadzenie 10.1. Przetwarzanie ciągów spełniających warunki wyrażenia regularnego 10.2. Wyodrębnianie grup z obiektu MatchCollection 10.3. Weryfikacja składni wyrażenia regularnego 10.4. Szybki sposób wyszukiwania ostatniego podciągu spełniającego kryteria 10.5. Zastępowanie znaków lub słów w ciągu znaków 10.6. Ulepszanie prostej funkcji do zastępowania ciągów znaków 10.7. Implementacja lepszego tokenizera 10.8. Kompilacja wyrażeń regularnych 10.9. Zliczanie wierszy tekstu 10 | Spis treści 10.10. Zwracanie całych wierszy w przypadku znalezienia podciągu pasującego do wzorca 10.11. Wyszukiwanie określonego wystąpienia pasującego podciągu 10.12. Wykorzystanie często używanych wzorców 10.13. Dokumentowanie wyrażeń regularnych 10.14. Zastosowanie wbudowanych wyrażeń regularnych do analizy stron ASP.NET 548 551 553 556 557 11. Algorytmy i struktury danych .................................................................................... 563 563 563 571 578 584 588 596 608 619 11.0. Wprowadzenie 11.1. Tworzenie skrótów dla typów danych 11.2. Tworzenie kolejek z priorytetami 11.3. Tworzenie kolejek dwukierunkowych 11.4. Sprawdzanie zrównoważenia znaków lub ciągów 11.5. Tworzenie odwzorowania jeden do wielu 11.6. Tworzenie drzew binarnych 11.7. Tworzenie drzewa n-arnego 11.8. Tworzenie obiektu Set 12. Operacje wejścia-wyjścia w systemie plików .......................................................... 631 631 632 634 637 638 639 645 649 650 657 657 659 662 663 667 669 671 672 676 677 679 682 12.0. Wprowadzenie 12.1. Tworzenie, kopiowanie, przenoszenie lub usuwanie pliku 12.2. Operacje na atrybutach plików 12.3. Zmiana nazwy pliku 12.4. Ustalanie, czy plik istnieje 12.5. Wybór metody otwarcia pliku lub strumienia dla zapisu i (lub) odczytu 12.6. Losowy dostęp do części pliku 12.7. Generowanie znaku EOL niezależnego od platformy 12.8. Tworzenie pliku, zapisywanie do niego i odczytywanie z niego 12.9. Ustalanie, czy istnieje katalog 12.10. Tworzenie, kopiowanie, przenoszenie i usuwanie katalogu 12.11. Operacje na atrybutach katalogów 12.12. Zmiana nazwy katalogu 12.13. Wyszukiwanie katalogów lub plików przy użyciu symboli wieloznacznych 12.14. Odczytywanie drzewa katalogów 12.15. Parsowanie ścieżki dostępu 12.16. Parsowanie ścieżek dostępu w zmiennych środowiskowych 12.17. Weryfikacja ścieżki dostępu 12.18. Używanie w aplikacji pliku tymczasowego 12.19. Otwieranie strumienia pliku przy użyciu jedynie uchwytu pliku 12.20. Jednoczesne zapisywanie do wielu plików wyjściowych 12.21. Uruchamianie i używanie narzędzi konsoli Spis treści | 11 12.22. Blokowanie części pliku 12.23. Wyszukiwanie w systemie plików konkretnych zmian w jednym lub więcej plikach lub katalogach 12.24. Oczekiwanie na wykonanie określonej czynności w systemie plików 12.25. Porównywanie wersji dwóch modułów wykonywalnych 12.26. Uzyskiwanie informacji o wszystkich napędach obecnych w systemie 12.27. Szyfrowanie i deszyfracja istniejącego pliku 12.28. Kompresowanie i dekompresja plików 683 686 691 694 697 700 701 13. Odzwierciedlanie ........................................................................................................ 705 705 705 708 709 713 715 13.0. Wprowadzenie 13.1. Odczytywanie listy podzespołów zależnych 13.2. Odczytywanie listy eksportowanych typów 13.3. Odnajdywanie metod pokrytych 13.4. Odnajdywanie składowych w podzespole 13.5. Odnajdywanie składowych w interfejsie 13.6. Ustalanie i odczytywanie typów zagnieżdżonych znajdujących się w podzespole 13.7. Wyświetlanie hierarchii dziedziczenia typu 13.8. Odnajdywanie podklas typu 13.9. Odnajdywanie w podzespole wszystkich typów, które można serializować 13.10. Filtrowanie danych w trakcie odczytywania składowych 13.11. Dynamiczne wywoływanie składowych 13.12. Definiowanie wskazówek dla zaciemniaczy kodu 13.13. Ustalanie, czy typ lub metoda ma charakter ogólny 13.14. Odczytywanie manifestu zasobów w kodzie źródłowym 13.15. Odczytywanie informacji o zmiennych lokalnych 13.16. Tworzenie typu ogólnego 716 718 720 721 723 727 730 732 734 735 737 14. Sieć WWW ................................................................................................................... 739 739 739 740 741 744 746 748 750 751 753 755 756 14.0. Wprowadzenie 14.1. Odczytywanie nazwy komputera na podstawie adresu IP 14.2. Odczytywanie adresu IP komputera o podanej nazwie 14.3. Parsowanie URI 14.4. Formowanie i weryfikacja URI bezwzględnego 14.5. Obsługa błędów serwera WWW 14.6. Komunikacja z serwerem WWW 14.7. Przesyłanie żądań przez serwer proxy 14.8. Odczytywanie kodu HTML z podanego adresu URL 14.9. Wykorzystanie nowej kontrolki przeglądarki internetowej 14.10. Wiązanie tabel baz danych z pamięcią podręczną 14.11. Zapisywanie w pamięci podręcznej danych z wieloma powiązaniami 12 | Spis treści 14.12. Prekompilacja strony ASP.NET z poziomu kodu źródłowego 14.13. Uwzględnianie i pomijanie sekwencji ucieczki w danych dla sieci WWW 14.14. Wykorzystanie klasy UriBuilder 14.15. Analiza i zmiana konfiguracji aplikacji sieciowej 14.16. Praca z kodem HTML 14.17. Zwiększanie wydajności pracy z HTTP przez zapisywanie wyników w pamięci podręcznej 14.18. Sprawdzanie własnych stron obsługi błędów używanych przez serwer 14.19. Odczytywanie odwzorowań aplikacji dla ASP.NET zdefiniowanych na serwerze IIS 758 761 763 765 767 770 771 774 15. XML ...............................................................................................................................777 777 15.0. Wprowadzenie 15.1. Wczytywanie i dostęp do danych XML w kolejności wyznaczonej w dokumencie 15.2. Odczyt dokumentu XML z sieci WWW 15.3. Wyszukiwanie informacji w dokumencie XML 15.4. Weryfikacja poprawności danych XML 15.5. Tworzenie dokumentu XML z poziomu kodu źródłowego 15.6. Wykrywanie zmian w dokumencie XML 15.7. Obsługa niedozwolonych znaków w ciągu znaków XML 15.8. Przekształcanie danych XML 15.9. Dzielenie dokumentu XML na części 15.10. Składanie dokumentu XML z części 15.11. Weryfikacja poprawności zmienionego dokumentu XML bez jego ponownego ładowania 15.12. Rozszerzanie przekształceń XSLT 15.13. Odczytywanie schematu z istniejących plików XML 15.14. Przekazywanie parametrów do transformacji XSLT 777 780 782 784 789 791 794 796 800 804 808 810 813 815 16.0. Wprowadzenie 16.1. Tworzenie serwera TCP 16.2. Tworzenie klienta TCP 16.3. Symulowanie przetwarzania formularza 16.4. Pobieranie danych z serwera 16.5. Komunikacja przy użyciu potoków nazwanych 16.6. Pingowanie z poziomu kodu źródłowego 16.7. Wysyłanie poczty SMTP przy użyciu usługi SMTP 16.8. Sprawdzanie parametrów dostępu do sieci 16.9. Skanowanie portów komputera przy użyciu gniazd 16. Praca w sieci ................................................................................................................ 819 819 819 824 827 830 831 850 852 856 861 865 871 16.10. Używanie bieżących ustawień połączenia z Internetem 16.11. Pobieranie pliku za pośrednictwem FTP Spis treści | 13 17. Bezpieczeństwo .......................................................................................................... 873 873 873 881 885 889 892 895 900 901 907 909 912 17.0. Wprowadzenie 17.1. Kontrola dostępu do typów w podzespole lokalnym 17.2. Szyfrowanie i rozszyfrowywanie ciągu znaków 17.3. Szyfrowanie i rozszyfrowywanie pliku 17.4. Usuwanie danych dotyczących szyfrowania 17.5. Sprawdzenie, czy ciąg znaków nie uległ uszkodzeniu w trakcie transmisji 17.6. Przesłanianie mechanizmu dodającego wartość mieszającą do ciągu znaków 17.7. Ulepszony generator liczb losowych 17.8. Bezpieczne przechowywanie danych 17.9. Zabezpieczanie asertacji bezpieczeństwa 17.10. Zapobieganie niepożądanym zmianom w podzespole 17.11. Sprawdzanie, czy podzespołowi nadano odpowiednie uprawnienia 17.12. Minimalizowanie zakresu uprawnień podzespołu umożliwiających przeprowadzenie ataku 17.13. Uzyskiwanie informacji dotyczących monitorowania i zabezpieczeń 17.14. Nadawanie i odbieranie dostępu do pliku lub klucza rejestru 17.15. Zabezpieczanie danych w postaci ciągów znaków 17.16. Zabezpieczanie strumienia danych 17.17. Szyfrowanie danych w pliku web.config 17.18. Rozpoznawanie pełnej przyczyny zgłoszenia wyjątku SecurityException 17.19. Zabezpieczanie procesu kodowania Unicode 17.20. Pozyskiwanie bezpieczniejszego uchwytu pliku 913 914 919 921 924 931 933 935 936 18. Wątki i synchronizacja ...............................................................................................939 939 939 942 947 949 952 18.0. Wprowadzenie 18.1. Tworzenie pól statycznych dla konkretnych wątków 18.2. Zapewnianie dostępu o bezpiecznych wątkach do składowych klasy 18.3. Zapobieganie cichemu zakończeniu wątków 18.4. Odpytywanie asynchronicznej metody delegowanej 18.5. Definiowanie czasu wygasania asynchronicznej metody delegowanej 18.6. Uzyskiwanie powiadomienia o zakończeniu działania asynchronicznej metody delegowanej 18.7. Ustalanie, czy żądanie skierowane do puli wątków zostanie zakolejkowane 18.8. Konfigurowanie licznika czasu 18.9. Bezpieczne przechowywanie danych wątku 18.10. Przydzielanie dostępu do zasobu więcej niż jednemu klientowi przy użyciu semafora 18.11. Synchronizowanie wielu procesów przy użyciu muteksu 18.12. Zapewnianie współpracy między wątkami za pomocą zdarzeń 18.13. Uzyskiwanie możliwości nadawania nazw własnym zdarzeniom 18.14. Wykonywanie operacji atomowych wśród wątków 954 957 959 962 965 969 979 981 984 14 | Spis treści 19. Kod niezabezpieczony ............................................................................................... 987 987 988 991 992 994 995 996 998 1000 1001 19.0. Wprowadzenie 19.1. Kontrolowanie zmian we wskaźnikach przekazywanych do metod 19.2. Porównywanie wskaźników 19.3. Nawigowanie po tablicach 19.4. Operacje na wskaźniku na tablicę stałą 19.5. Zwracanie wskaźnika na konkretny element tablicy 19.6. Tworzenie i używanie tablicy wskaźników 19.7. Zamiana nieznanych typów wskaźników 19.8. Przekształcanie ciągu znaków w char* 19.9. Deklarowanie struktury o stałym rozmiarze z osadzoną tablicą 20. Przybornik ................................................................................................................. 1003 1003 20.0. Wprowadzenie 20.1. Obsługa procesów zamknięcia systemu, zarządzania mocą lub zmian w sesji użytkownika 20.2. Sterowanie usługą 20.3. Uzyskiwanie listy procesów, w których załadowano podzespół 20.4. Używanie kolejek komunikatów na lokalnej stacji roboczej 20.5. Odnajdywanie ścieżki do bieżącej wersji .NET Framework 20.6. Ustalanie wersji zarejestrowanych w globalnej pamięci podręcznej podzespołów 20.7. Odczytywanie ścieżki do katalogu Windows 20.8. Przechwytywanie danych wyjściowych ze standardowego strumienia wyjścia 20.9. Uruchamianie kodu w jego własnej domenie AppDomain 20.10. Ustalanie wersji systemu operacyjnego oraz pakietu Service Pack 1003 1007 1010 1012 1015 1015 1018 1018 1021 1022 Skorowidz ..................................................................................................................1027 Spis treści | 15 ROZDZIAŁ 1. 1.0. Wprowadzenie Typy proste to podzbiór wbudowanych typów w języku C#. W rzeczywistości typy te zdefi- niowano w bibliotece .NET Framework Class Library (.NET FCL). Na typy proste składa się kilka typów liczbowych oraz typ bool. Do typów liczbowych należy typ dziesiętny (decimal), dzie- więć typów liczb całkowitych (byte, char, int, long, sbyte, short, uint, ulong, ushort) oraz dwa typy zmiennoprzecinkowe (float, double). Typy proste wraz z ich pełną, kwalifikowaną nazwą na platformie .NET Framework zestawiono w tabeli 1.1. Tabela 1.1. Proste typy danych Pełna nazwa System.Boolean System.Byte System.SByte System.Char Alias bool byte sbyte char Zakres wartości true lub false 0 – 255 –128 – 127 0 – 65535 System.Decimal decimal –79 228 162 514 264 337 593 543 850 335 – 79 228 162 514 264 337 593 543 950 335 System.Double double –1,79769313486232e308 – 1,79769313486232e308 System.Single System.Int16 float short –3,40282347E+38 – 3,40282347E+38 –32 768 – 32 767 System.Uint16 ushort 0 – 65535 System.Int32 System.UInt32 System.Int64 int uint long –2 147 483 648 – 2 147 483 647 0 – 4 294 967 295 –9 223 372 036 854 775 808 – 9 223 372 036 854 775 807 System.UInt64 ulong 0 – 18 446 744 073 709 551 615 Słowa kluczowe w języku C# dla różnych typów danych to jedynie aliasy ich pełnych, kwali- fikowanych nazw. Nie ma znaczenia, czy w kodzie wykorzystamy pełną nazwę typu czy też słowo kluczowe: kompilator języka C# w obu przypadkach wygeneruje identyczny kod. Należy zwrócić uwagę, że następujące nazwy typów: sbyte, ushort, uint i ulong, nie są zgodne z ogólną specyfikacją języka (ang. Common Language Specification — CLS). W efekcie 27 mogą być nieobsługiwane w innych językach wchodzących w skład platformy .NET. Brak obsługi tych typów może ograniczyć możliwości komunikacji kodu C# z kodem napisanym w innych językach zgodnych z CLS, na przykład w języku Visual Basic .NET. Typy wyliczeniowe niejawnie dziedziczą własności po typie System.Enum, który z kolei dzie- dziczy własności po typie System.ValueType. Typy wyliczeniowe mają jedno zastosowanie: opisanie elementów określonego zbioru; na przykład kolory czerwony (ang. red), niebieski (ang. blue) i żółty (ang. yellow) można zdefiniować jako elementy typu wyliczeniowego Va- lidShapeColor. W podobny sposób można zdefiniować figury: kwadrat (ang. square), okrąg (ang. circle) i trójkąt (ang. triangle) jako elementy typu wyliczeniowego ValidShape. Definicja tych typów może mieć następującą postać: enum ValidShapeColor { Red, Blue, Yellow } enum ValidShape { Square = 2, Circle = 4, Triangle = 6 } Każdy element typu wyliczeniowego otrzymuje wartość liczbową niezależnie od tego, czy się ją jawnie przypisze czy nie. Ponieważ kompilator automatycznie nadaje elementom typów wyliczeniowych wartości począwszy od zera i zwiększa je o jeden dla każdego kolejnego ele- mentu, zdefiniowany wcześniej typ wyliczeniowy ValidShapeCreator można by równie dobrze zdefiniować w sposób następujący: enum ValidShapeColor { Red = 0, Blue = 1, Yellow = 2 } Typy wyliczeniowe to dobre narzędzia do dokumentowania kodu. Na przykład o wiele bardziej intuicyjnie zrozumiały będzie kod zapisany w sposób następujący: ValidShapeColor currentColor = ValidShapeColor.Red; niż zapisany tak: int currentColor = 0; Jedna i druga wersja jest prawidłowa, ale kod zapisany pierwszą metodą lepiej się czyta i jest bardziej zrozumiały. Ma to szczególne znaczenie w przypadku, gdy programista jest zmuszony do czytania kodu, który napisał ktoś inny. Kod w pierwszej wersji jest również bezpieczny pod względem typów (ang. type-safe), podczas gdy kod, w którym zastosowano wartości int, nie charakteryzuje się taką własnością. 1.1. Określanie przybliżonej równości pomiędzy wartością ułamkową a zmiennoprzecinkową Problem Trzeba porównać ułamek z wartością typu double lub float po to, by określić, czy ich wartości są sobie bliskie. Weźmy na przykład wynik porównania wyrażenia 1/6 z wartością 0,16666667. 28 | Rozdział 1. Liczby i typy wyliczeniowe Wydaje się, że liczby te są sobie równe, jednak liczba 0,16666667 jest wyrażona z dokładno- ścią jedynie do 8 pozycji po przecinku, natomiast dokładność wyrażenia 1/6 jest ograniczona maksymalną liczbą cyfr po przecinku, jakie można zapisać w określonym typie danych. Rozwiązanie W celu porównania przybliżonej równości pomiędzy wartością ułamkową a zmiennoprzecin- kową sprawdzimy, czy różnica pomiędzy obiema wartościami mieści się w dopuszczalnych granicach: using System; // Wersja, w której wykorzystano wartość epsilon typu System.Double.Epsilon public static bool IsApproximatelyEqualTo(double numerator, double denominator, double dblValue) { return IsApproximatelyEqualTo(numerator, denominator, dblValue, double.Epsilon); } // Wersja, w której można wprowadzić wartość epsilon // innego typu niż System.Double.Epsilon public static bool IsApproximatelyEqualTo(double numerator, double denominator, double dblValue, double epsilon) { double difference = (numerator/denominator) - dblValue; if (Math.Abs(difference) epsilon) { // Dobre przybliżenie. return true; } else { // To NIE jest dobre przybliżenie. return false; } } Wystarczy zastąpić typ double typem float, aby stwierdzić, czy ułamek jest w przybliżeniu równy wartości typu float. Analiza Wartości ułamkowe można przedstawić, dzieląc licznik przez mianownik, jednak czasami konieczne jest ich zapisywanie w postaci liczb zmiennoprzecinkowych. W takim przypadku pojawiają się problemy związane z zaokrągleniami, które utrudniają porównywanie. Wyrażanie wartości w postaci ułamkowej (np. 1/6) zapewnia maksymalną dokładność. Wyrażanie wartości w postaci zmiennoprzecinkowej (np. 0,1667) ogranicza tę dokładność. W takim przypadku dokładność zależy od liczby cyfr, jaką programista użyje po prawej stronie kropki dziesiętnej. W niektórych przypadkach trzeba stwierdzić, czy dwie wartości są w przybliżeniu sobie równe. Takie porównanie można osiągnąć poprzez zdefiniowanie wartości (epsilon), która reprezentuje najmniejszą dodatnią wartość dopuszczalnej różnicy pomiędzy dwiema liczbami, aby w dalszym ciągu były uznawane za równe. Mówiąc inaczej, wystarczy wyliczyć wartość bezwzględną 1.1. Określanie przybliżonej równości pomiędzy wartością ułamkową a zmiennoprzecinkową | 29 różnicy pomiędzy wartością ułamkową (nominator/denominator) a wartością zmiennoprze- cinkową (dblValue) i porównać ją z predefiniowaną wartością przekazaną jako argument epsilon, aby stwierdzić, czy wartość zmiennoprzecinkowa jest dobrym przybliżeniem wartości ułamkowej. Przeanalizujmy porównanie pomiędzy ułamkiem 1/7 a wartością zmiennoprzecinkową 0,14285714285714285. Poniższe wywołanie metody IsApproximatelyEqualTo wskazuje na to, że w wartości zmiennoprzecinkowej jest zbyt mało cyfr po prawej stronie kropki dziesiętnej, aby było ono dobrym przybliżeniem (jest sześć cyfr, a potrzebnych jest siedem): bool Approximate = Class1.IsApproximatelyEqualto(1, 7, .142857, .0000001); // Approximate == false Po wprowadzeniu kolejnej cyfry dokładności do trzeciego parametru metody okazało się, że uzyskaliśmy dobre przybliżenie ułamka 1/7: bool Approximate = Class1.IsApproximatelyEqualto(1, 7, .1428571, .0000001); // Approximate == true Zobacz również Tematy Double.Epsilon Field i Single.Epsilon Field w dokumentacji MSDN. · 1.2. Konwersja stopni na radiany Problem W przypadku korzystania z funkcji trygonometrycznych klasy Math wszystkie jednostki są wyrażone w radianach. Często mamy do czynienia z kątami mierzonymi w stopniach i aby skorzystać z metod klasy, musimy przekształcić je na radiany. Rozwiązanie Aby przekształcić wartość wyrażoną w stopniach na radiany, należy ją pomnożyć przez współ- czynnik π/180: using System; public static double ConvertDegreesToRadians (double degrees) { double radians = (Math.PI / 180) * degrees; return (radians); } Analiza We wszystkich statycznych metodach trygonometrycznych klasy Math kąty wyrażane są w ra- dianach. Procedury konwersji pomiędzy stopniami a radianami są bardzo przydatne, zwłaszcza gdy użytkownik wprowadza dane w stopniach, a nie w radianach. Pomimo wszystko lepiej rozumiemy stopnie od radianów. 30 | Rozdział 1. Liczby i typy wyliczeniowe Równanie konwersji stopni na radiany ma następującą postać: radians = (Math.PI / 180) * degrees Statyczne pole Math.PI zawiera stałą π. 1.3. Konwersja radianów na stopnie Problem W przypadku korzystania z funkcji trygonometrycznych klasy Math stopnie są wyrażone w radianach. Często zdarza się, że wyniki obliczeń trzeba podawać w stopniach. Rozwiązanie Aby przekształcić wartość wyrażoną w radianach na stopnie, należy ją pomnożyć przez współ- czynnik 180/π: using System; public static double ConvertRadiansToDegrees (double radians) { double degrees = (180 / Math.PI) * radians; return (degrees); } Analiza We wszystkich statycznych metodach trygonometrycznych klasy Math, kąty wyrażane są w radianach. Procedury konwersji pomiędzy stopniami a radianami są bardzo przydatne. Wyświetlanie wyników w stopniach jest bardziej zrozumiałe dla użytkowników. Równanie przekształcenia radianów na stopnie ma następującą postać: degrees = (180 / Math.PI) * radians Statyczne pole Math.PI zawiera stałą π. 1.4. Zastosowanie operatora negacji bitowej do danych różnych typów Problem Operator negacji bitowej (~) można przeciążyć w taki sposób, by można go było wykorzystać bezpośrednio z danymi typu int, uint, long, ulong, a także z danymi typów wyliczeniowych składających się z typów int, uint, long i ulong. Często jednak występuje potrzeba wyko- nywania operacji negacji bitowej na danych innych typów liczbowych. 1.4. Zastosowanie operatora negacji bitowej do danych różnych typów | 31 Rozwiązanie Aby skorzystać z bitowego operatora negacji dla danych dowolnego typu, należy dokonać konwersji uzyskanej wartości operacji bitowej na typ, który chcemy wykorzystywać. W po- niższym kodzie zaprezentowano tę technikę dla typu danych byte: byte y =1; byte result = (byte)~y; W efekcie wykonania powyższej operacji do zmiennej result przypisywana jest wartość 254. Analiza Poniższy kod pokazuje nieprawidłowe wykorzystanie operatora negacji bitowej z danymi typu byte: byte y = 1; Console.WriteLine( ~y = + ~y); W wyniku uruchomienia tego kodu nieoczekiwanie wyświetla się liczba –2. Najwyraźniej wynik wykonania operacji negacji bitowej dla zmiennej typu byte jest niepra- widłowy. Prawidłowy wynik to 254. W rzeczywistości byte jest typem danych bez znaku, a zatem nie może przyjmować wartości ujemnych. Jeśli zapiszemy kod w następujący sposób: byte y = 1; byte result = ~y; uzyskamy błąd kompilacji Cannot implicitly convert type ‘int’ to ‘byte’1. Ten komunikat o błędzie trochę wyjaśnia, dlaczego wcześniejsza operacja nie zadziałała tak, jak się spodziewaliśmy. Aby rozwiązać problem, trzeba jawnie przeprowadzić konwersję wyniku na typ byte przed przypisaniem jej do zmiennej result, tak jak pokazano poniżej: byte y = 1; byte result = (byte)~y; Powyższa konwersja jest konieczna, ponieważ operatory bitowe są przeciążane do działania tylko na sześciu typach danych: int, uint, long, ulong, bool oraz typach wyliczeniowych. Jeśli operator bitowy zostanie użyty w odniesieniu do danych innego typu, zostaną one przekształ- cane na obsługiwany typ danych, najbardziej zbliżony do typu, dla którego próbowano wy- konać operację. Dlatego właśnie przed wykonaniem negacji bitowej dane typu byte zostały przekształcone na int: 0x01 // byte y = 1; 0xFFFFFFFE // Wartość 01h jest przekształcana na int, a następnie // jest dla niej wykonywana negacja bitowa. // Taka kombinacja bitów dla danych typu int odpowiada liczbie –2. 0xFE // Dla uzyskanej wartości int wykonywana jest konwersja na typ byte. Zwróćmy uwagę na to, że int, w odróżnieniu od byte, jest typem danych ze znakiem. Dlatego właśnie w wyniku uzyskaliśmy –2 zamiast spodziewanej wartości 254. Konwersję typu danych byte na najbliższy odpowiednik określa się terminem promocji numerycznej (ang. numeric promotion). Mechanizm ten wykorzystuje się również w przypadku zastosowania danych różnych typów z operatorami dwuargumentowymi (w tym także bitowymi). 1 Nie można niejawnie przekształcać danych typu int na byte — przyp. tłum. 32 | Rozdział 1. Liczby i typy wyliczeniowe Mechanizm promocji numerycznej szczegółowo opisano w specyfikacji języka C# (C# Language Specification) w punkcie 7.2.6 (dokument jest dostępny pod adresem http://msdn.microsoft.com/vcsharp/programming/language). Dokładne zrozumienie działania promocji numerycznej ma istotne znaczenie w przypadku wykorzystania operatorów na danych różnych typów, a także w przypadku zastosowania operatorów z takimi typami danych, dla których nie istnieje przeciążona wersja operatora obsługująca je. Dzięki znajomości mechanizmu promocji numerycznej można zaoszczędzić wiele godzin debugowania. 1.5. Sprawdzenie, czy liczba jest parzysta czy nieparzysta Problem Potrzebna jest szybka metoda umożliwiająca stwierdzenie, czy wartość liczbowa jest parzysta czy nieparzysta. Rozwiązanie Rozwiązanie można zaimplementować w postaci dwóch metod. W celu sprawdzenia, czy liczba jest parzysta, można wykorzystać następującą metodę: public static bool IsEven(int intValue) { return ((intValue 2) == 0); } Aby sprawdzić, czy liczba jest nieparzysta, można zastosować następującą metodę: public static bool IsOdd(int intValue) { return((intValue 2) == 1); } Analiza W każdej liczbie nieparzystej najmniej znaczący bit zawsze ma wartość 1. Właśnie dlatego, aby stwierdzić, że określona liczba jest nieparzysta, wystarczy skontrolować, czy jej najmniej znaczący bit ma wartość 1. Z kolei poprzez sprawdzenie, czy najmniej znaczący bit ma war- tość 0, można stwierdzić, czy liczba jest parzysta. W celu sprawdzenia, czy wartość jest parzysta, wykonujemy operację AND tej wartości z liczbą 1, a następnie kontrolujemy, czy wynik tej operacji jest zerem. Jeśli tak, możemy stwierdzić, że testowana liczba jest parzysta, a jeśli nie, jest nieparzysta. Opisaną powyżej operację wykonano w metodzie IsEven. Z kolei aby sprawdzić, czy wartość jest nieparzysta, należy podobnie jak poprzednio wykonać operację AND tej wartości z liczbą 1 i skontrolować, czy uzyskany wynik ma wartość 1. Jeśli tak, możemy stwierdzić, że testowana liczba jest nieparzysta, w przeciwnym przypadku jest parzysta. Opisywaną operację wykonano w metodzie IsOdd. 1.5. Sprawdzenie, czy liczba jest parzysta czy nieparzysta | 33 Należy zwrócić uwagę, że w danej aplikacji nie ma potrzeby implementacji obu metod — IsOdd i IsEven — choć implementacja ich obydwu z pewnością zwiększy czytelność kodu. Bez trudu można zaimplementować jedną z metod, używając drugiej. Oto przykład implementacji metody IsOdd za pomocą metody IsEven: public static bool IsOdd(int intValue) { return (!IsEven(intValue)); } Pokazane powyżej metody można stosować wyłącznie wobec 32-bitowych liczb całkowitych. Aby zastosować je w odniesieniu do innych typów liczbowych, wystarczy je przeciążyć dla dowolnego potrzebnego typu. Na przykład, aby stwierdzić, czy 64-bitowa liczba całkowita jest parzysta, można zmodyfikować metodę IsEven w sposób następujący: public static bool isEven(long longValue) { return ((longValue 1) == 0); } Wystarczyło zmodyfikować typ danych na liście parametrów. 1.6. Uzyskanie bardziej znaczącego i mniej znaczącego słowa liczby Problem Mamy 32-bitową liczbę całkowitą zawierającą informacje zarówno w mniej znaczących, jak i w bardziej znaczących 16 bitach. Potrzebujemy metody, która odczytałaby z tej liczby bardziej znaczące słowo (pierwsze 16 bitów) i (lub) mniej znaczące słowo (ostatnie 16 bitów). Rozwiązanie Aby uzyskać bardziej znaczące słowo liczby całkowitej, należy wykonać bitową operacją AND tej liczby z wartością 0xFFFF 16, tak jak pokazano poniżej: public static int GetHighWord(int intValue) { return (intValue (0xFFFF 16)); } Aby uzyskać mniej znaczące słowo liczby, można skorzystać z następującej metody: public static int GetLowWord(int intValue) { return (intValue 0x0000FFFF); } Technikę tę można z łatwością zmodyfikować do zastosowania z liczbami całkowitymi innych rozmiarów (8-bitowych, 16-bitowych bądź 64-bitowych). Sposoby wykonania tych działań omówiono w punkcie „Analiza”. 34 | Rozdział 1. Liczby i typy wyliczeniowe Analiza Aby wyznaczyć wartość bardziej znaczącego słowa liczby, wystarczy skorzystać z następującej bitowej operacji AND: uint intValue = Int32.MaxValue; uint MSB = intValue (0xFFFF 16); // MSB == 0xFFFF0000 Powyższa metoda wykonuje operację AND liczby z inną liczbą — taką, w której wszystkie bity bardziej znaczącego słowa są ustawione na 1. Wykonanie tej metody spowoduje wyzerowanie wszystkich bitów mniej znaczącego słowa, natomiast bity słowa bardziej znaczącego pozostaną bez zmian. Aby wyznaczyć wartość mniej znaczącego słowa liczby, można skorzystać z następującej operacji AND: uint intValue = Int32.MaxValue; uint LSB = intValue 0x0000FFFF; // LSB == 0x0000FFFF Powyższa metoda wykonuje operację AND liczby z inną liczbą — taką, w której wszystkie bity mniej znaczącego słowa są ustawione na 1. Wykonanie tej metody spowoduje wyzerowanie wszystkich bitów bardziej znaczącego słowa, natomiast bity mniej znaczącego słowa pozostaną bez zmian. Pokazane powyżej metody można stosować wyłącznie do 32-bitowych liczb całkowitych. Aby zastosować je w odniesieniu do innych typów liczbowych, wystarczy je przeciążyć dla dowol- nego potrzebnego typu. Na przykład, aby wyznaczyć mniej i bardziej znaczące słowa liczby 16-bitowej, można skorzystać z metody o podobnej strukturze do metody GetHighWord: public static short GetHighByte(short shortValue) { return (short)(shortValue (0xFF 8)); } Metodę GetLowWord można zmodyfikować następująco: public static short GetLowByte(short shortValue) { return (short)(shortValue (short)0xFF); } 1.7. Konwersja liczby z innego systemu liczbowego na dziesiętny Problem Mamy ciąg znaków zawierający liczbę wyrażoną w systemie dwójkowym, ósemkowym, dzie- siętnym lub szesnastkowym. Chcemy przekształcić ten ciąg znaków na odpowiadającą mu liczbę całkowitą i wyświetlić w postaci dziesiętnej. 1.7. Konwersja liczby z innego systemu liczbowego na dziesiętny | 35 Rozwiązanie Aby przekształcić liczbę wyrażoną w innym systemie liczbowym na system dziesiętny, wy- starczy skorzystać z przeciążonej wersji statycznej metody Convert.ToInt32 należącej do klasy Convert: string base2 = 11 ; string base8 = 17 ; string base10 = 110 ; string base16 = 11FF ; Console.WriteLine( Convert.ToInt32(base2, 2) = + Convert.ToInt32(base2, 2)); Console.WriteLine( Convert.ToInt32(base8, 8) = + Convert.ToInt32(base8, 8)); Console.WriteLine( Convert.ToInt32(base10, 10) = + Convert.ToInt32(base10, 10)); Console.WriteLine( Convert.ToInt32(base16, 16) = + Convert.ToInt32(base16, 16)); Wykonanie powyższego kodu spowoduje wyświetlenie następującego wyniku: Convert.ToInt32(base2, 2) = 3 Convert.ToInt32(base8, 8) = 15 Convert.ToInt32(base10, 10) = 110 Convert.ToInt32(base16, 16) = 4607 Analiza Dla statycznej metody Convert.ToInt32 istnieje przeciążona wersja pobierająca ciąg zawie- rający liczbę oraz liczbę całkowitą definiującą podstawę systemu liczbowego, w jakim tę liczbę wyrażono. Następnie metoda przekształca ciąg zawierający liczbę na wartość całkowitą typu integer, po czym metoda Console.WriteLine przekształca liczbę na postać dziesiętną i ją wyświetla. Dla innych statycznych metod klasy Convert, takich jak ToByte, ToInt64 oraz ToInt16, istnieją podobne przeciążone wersje, które pobierają liczbę w postaci ciągu znaków oraz podstawę systemu liczbowego, w którym wyrażono tę liczbę. Niestety, metody te jedynie przekształcają znakowe reprezentacje liczb wyrażonych w systemach dwójkowym, ósemkowym, dziesiętnym i szesnastkowym na postać dziesiętną. Nie pozwalają na konwersję liczby na ciąg znaków wyrażony w innym systemie liczbowym niż dziesiętny. Na taką konwersję pozwalają jednak metody ToString dostępne dla różnych typów liczbowych. Zobacz również Tematy Convert Class oraz Converting with System.Convert w dokumentacji MSDN. · 36 | Rozdział 1. Liczby i typy wyliczeniowe 1.8. Sprawdzenie, czy ciąg znaków reprezentuje prawidłową liczbę Problem Mamy ciąg znaków, który być może zawiera wartość liczbową, Chcemy się przekonać, czy tak jest. Rozwiązanie Można zastosować metodę TryParse dowolnego typu liczbowego. Na przykład, aby sprawdzić, czy ciąg zawiera wartość typu double, można zastosować następującą metodę: string str = 12.5 ; double result = 0; if(double.TryParse(str, System.Globalization.NumberStyles.Float, System.Globalization.NumberFormatInfo.CurrentInfo, out result)) { // liczba jest typu double! } Analiza W tej recepturze pokazano, w jaki sposób stwierdzić, czy ciąg znaków zawiera wyłącznie wartość liczbową. Metoda TryParse zwraca true w sytuacji, gdy ciąg znaków zawiera po- prawną liczbę, bez zgłaszania wyjątku właściwego dla metody Parse. Ponieważ metoda TryParse nie zgłasza wyjątków, działa wydajniej w przypadku jej zastosowania dla szeregu ciągów znaków, z których niektóre nie zawierają liczb. Zobacz również Tematy Parse i TryParse w dokumentacji MSDN. · 1.9. Zaokrąglanie wartości zmiennoprzecinkowych Problem Trzeba zaokrąglić liczbę do wartości całkowitej bądź do określonej liczby miejsc po przecinku. Rozwiązanie W celu zaokrąglenia dowolnej liczby do najbliższej liczby całkowitej, można zastosować prze- ciążoną wersję statycznej metody Round, która pobiera pojedynczy argument: int x = (int)Math.Round(2.5555); // x == 3 1.9. Zaokrąglanie wartości zmiennoprzecinkowych | 37 Aby zaokrąglić wartość zmiennoprzecinkową do określonej liczby miejsc po przecinku, można skorzystać z przeciążonej wersji metody Math.Round pobierającej dwa argumenty: decimal x = Math.Round(2.5555, 2); // x == 2.56 Analiza Metoda Round jest łatwa w użyciu, podczas jej stosowania trzeba jednak pamiętać, w jaki sposób działa operacja zaokrąglania. Metoda Round jest zgodna z punktem 4. standardu IEE 754. Oznacza to, że jeśli zaokrąglana liczba znajduje się w połowie pomiędzy dwoma liczbami, operacja Round zawsze zaokrągli ją do wartości parzystej. Oto przykład: decimal x = Math.Round(1.5); // x == 2 decimal y = Math.Round(2.5); // y == 2 Zwróćmy uwagę, że liczbę 1,5 zaokrąglono w górę do najbliższej całkowitej liczby parzystej, natomiast liczbę 2,5 zaokrąglono w dół do najbliższej całkowitej liczby parzystej. Należy o tym pamiętać podczas korzystania z operacji Round. Zobacz również Temat Math Class w dokumentacji MSDN. · Receptury 1.1 i 1.22. · 1.10. Wybór algorytmu zaokrąglania Problem W przypadku zastosowania metody Math.Round liczba 1,5 zostanie zaokrąglona do wartości 2, podobnie jak liczba 2,5. Często zdarza się, że w takiej sytuacji chcemy zaokrąglać w górę (tzn. 2,5 po zaokrągleniu ma wynosić 3, a nie 2) lub w dół (tzn. 1,5 po zaokrągleniu ma mieć wartość 1). Rozwiązanie Aby zawsze zaokrąglać w górę w przypadku, gdy wartość liczby leży dokładnie w połowie pomiędzy dwiema liczbami całkowitymi, należy skorzystać z metody Math.Floor: public static double RoundUp(double valueToRound) { return (Math.Floor(valueToRound + 0.5)); } Aby zawsze zaokrąglać w dół liczby leżące dokładnie w połowie pomiędzy dwiema warto- ściami całkowitymi, można zastosować następującą technikę: public static double RoundDown(double valueToRound) { double floorValue = Math.Floor(valueToRound); if ((valueToRound - floorValue) .5) { return (floorValue + 1); } 38 | Rozdział 1. Liczby i typy wyliczeniowe else { return (floorValue); } } Analiza Statyczna metoda Math.Round zaokrągla liczby w górę do najbliższej liczby parzystej (więcej informacji można znaleźć w recepturze 1.9). Czasami jednak są sytuacje, kiedy nie chcemy zaokrąglać liczb w ten sposób. Aby zastosować inny sposób zaokrąglania, można skorzystać ze statycznej metody Math.Floor. Należy zwrócić uwagę, że metody zastosowane do zaokrąglania liczb w tej recepturze nie zaokrąglają do określonej liczby miejsc dziesiętnych, ale do najbliższej liczby całkowitej. Zobacz również Temat Math Class w dokumentacji MSDN. · Receptury 1.9 i 1.22. · 1.11. Zamiana stopni Celsjusza na stopnie Fahrenheita Problem Odczytaliśmy temperaturę w stopniach Celsjusza i chcemy zamienić ją na stopnie Fahrenheita. Rozwiązanie public static double CtoF(double celsius) { return(((0.9/0.5) * celsius) +32); } Aby wygenerować wynik typu double z zachowaniem tego samego współczynnika jak dla liczb całkowitych (9 do 5), w obliczeniach wykorzystano liczby 0.9 i 0.5. Analiza W niniejszej recepturze wykorzystano następujące równanie konwersji stopni Celsjusza na stopnie Fahrenheita: TempFahrenheit = ((9 / 5) * TempCelsius) + 32 Skalę temperatury Fahrenheita powszechnie stosuje się w Stanach Zjednoczonych. W większości pozostałej części świata zazwyczaj stosuje się stopnie Celsjusza. 1.11. Zamiana stopni Celsjusza na stopnie Fahrenheita | 39 1.12. Zamiana stopni Fahrenheita na stopnie Celsjusza Problem Odczytaliśmy temperaturę w stopniach Fahrenheita i chcemy zamienić ją na stopnie Celsjusza. Rozwiązanie public static double FtoC(double fahrenheit) { return(((0.5/0.9) * fahrenheit) - 32); } Analiza W niniejszej recepturze wykorzystano następujące równanie konwersji stopni Fahrenheita na Celsjusza: TempCelsius = (0.5 / 0.9) * (TempFahrenheit – 32) Skalę temperatury Fahrenheita powszechnie stosuje się w Stanach Zjednoczonych. W więk- szości pozostałej części świata zazwyczaj stosuje się stopnie Celsjusza. 1.13. Bezpieczna konwersja liczb do mniejszego rozmiaru Problem Trzeba dokonać konwersji liczby o większym rozmiarze do mniejszego formatu bez utraty informacji. Na przykład konwersja liczby typu long na int powoduje utratę informacji tylko wtedy, gdy liczba typu long poddawana konwersji jest większa od int.MaxSize. Rozwiązanie Najprostsze rozwiązanie problemu polega na skorzystaniu ze słowa kluczowego checked. W zamieszczonej poniżej metodzie pobrano dwa argumenty typu long i podjęto próbę do- dania ich do siebie. Wynik ma być zwrócony jako liczba całkowita. Jeśli nastąpi przepełnienie, metoda zgłosi wyjątek OverflowException: using System; public static void UseChecked(long lhs, long rhs) { int result = 0; try { result = checked((int)(lhs + rhs)); } 40 | Rozdział 1. Liczby i typy wyliczeniowe catch (OverflowException e) { // Obsługa wyjątku przepełnienia. } } Jest to najprostsza metoda. Jeśli jednak nie chcemy ponosić kosztów zgłaszania wyjątków i ujmo- wać kodu dużej objętości w bloku try-catch w celu obsłużenia przepełnienia, możemy wy- korzystać pola MaxValue i MinValue dla każdego typu danych. Przed konwersją można prze- prowadzić test z wykorzystaniem tych pól i się upewnić, czy nie nastąpi utrata informacji. Jeśli tak, można przekazać do aplikacji komunikat, że wykonywana konwersja doprowadzi do utraty informacji. Aby stwierdzić, czy wartość sourceValue da się bezpiecznie (bez utraty informacji) poddać konwersji na typ danych short, można skorzystać z następującej instrukcji warunkowej: // Deklaracja i zainicjowanie dwóch zmiennych. int sourceValue = 34000; short destinationValue = 0; // Sprawdzenie, czy konwersja zmiennej sourceValue na typ short doprowadzi do utraty informacji. if (sourceValue = short.MaxValue sourceValue = short.MinValue) { destinationValue = (short)sourceValue; } else { // Poinformowanie aplikacji o utracie informacji. } Analiza Konwersja zawężająca (ang. narrowing conversion) zachodzi w przypadku, gdy dana typu o większym rozmiarze jest przekształcana na typ o mniejszym rozmiarze. Może tak się zdarzyć, na przykład, w przypadku konwersji wartości typu Int32 na Int16. Jeśli wartość typu Int32 jest mniejsza bądź równa wartości pola Int16.MaxValue, natomiast wartość Int32 jest więk- sza bądź równa wartości pola Int16.MinValue, konwersja przebiegnie bez błędów oraz bez utraty informacji. Utrata informacji wystąpi w przypadku, gdy wartość typu Int32 będzie większa od wartości pola Int16.MaxValue lub wartość Int32 będzie mniejsza niż wartość pola Int16.MinValue. W każdym z tych przypadków najbardziej znaczące bity wartości Int32 zosta- ną obcięte i odrzucone, co spowoduje, że wartość po konwersji będzie inna niż przed konwersją. Jeśli w kodzie działającym w kontekście niekontrolowanym (ang. unchecked context) wystąpi utrata informacji, nie zostanie zgłoszony żaden wyjątek i aplikacja nie wykryje takiej sytuacji. Problem ten może spowodować powstanie błędów bardzo trudnych do wyśledzenia. Aby im zapobiec, należy sprawdzić, czy wartość, która ma być poddana konwersji, mieści się pomię- dzy dolnym a górnym limitem typu, na który ma nastąpić konwersja. Jeśli wartość leży poza tym zakresem, można napisać kod, który obsłuży tę sytuację. Taki kod pozwala na zrezygno- wanie z konwersji i (lub) poinformowanie aplikacji o potencjalnym problemie. Zaprezentowane rozwiązanie może zapobiec wystąpieniu w aplikacji trudnych do wykrycia błędów arytme- tycznych. Należy zwrócić uwagę, że obie techniki pokazane w punkcie „Rozwiązanie” są prawidłowe. Wykorzystana technika zależy jednak od tego, czy spodziewamy się częstych przypadków przepełnienia czy tylko pojedynczych. Jeśli sytuacje przepełnienia będą występować często, 1.13. Bezpieczna konwersja liczb do mniejszego rozmiaru | 41 lepiej wybrać drugą technikę polegającą na ręcznym sprawdzaniu wartości liczbowej. W innym przypadku lepiej skorzystać ze słowa kluczowego checked, tak jak pokazano w pierwszej z technik. W języku C# kod może działać w kontekście kontrolowanym (ang. checked) bądź niekontrolowanym (ang. unchecked). Domyślnie kod działa w kontekście niekontro- lowanym. W kontekście kontrolowanym wszystkie obliczenia arytmetyczne i operacje konwersji z wykorzystaniem typów całkowitych są sprawdzane pod kątem prze- pełnienia. Jeśli wystąpi przepełnienie, kod zgłasza wyjątek OverflowException. W kontekście niekontrolowanym w przypadku przepełnienia wyjątek Overflo- wException nie będzie zgłoszony. Kontekst kontrolowany można włączyć za pomocą opcji kompilatora /checked{+}, ustawiając właściwość projektu Check for Arithmetic Overflow/Underflow na wartość true lub za pomocą słowa kluczowego checked. Kontekst niekontrolowany można włączyć za pomocą opcji kompilatora /checked-, ustawiając właściwość projektu Check for Arithmetic Overflow/Underflow na wartość false lub za pomocą słowa klu- czowego unchecked. Zwróćmy uwagę, że w kodzie obsługującym konwersję na typy całkowite zaprezentowanym w tej recepturze nie uwzględniono typów zmiennoprzecinkowych. Jest tak dlatego, ponieważ konwersja z dowolnego typu całkowitego na typy float, double lub decimal nigdy nie po- woduje utraty informacji. W związku z tym wprowadzanie dodatkowych testów jest zbędne. Podczas wykonywania konwersji dodatkowo należy zwrócić uwagę na następujące zagadnienia: · Konwersja danych typu float, double lub decimal na typy całkowite powoduje obcięcie części ułamkowej liczby. Co więcej, jeśli część całkowita liczby przekracza wartość pola MaxValue typu docelowego, uzyskana wartość będzie niezdefiniowana, chyba że konwersja jest wykonywana w kontekście kontrolowanym. W takim przypadku nastąpi zgłoszenie wyjątku OverflowException. Konwersja danych typu float lub double na decimal powoduje zaokrąglenie danych typu · float lub double do 28 miejsc po przecinku. Konwersja danych z typu double na float powoduje zaokrąglenie wartości typu double · do najbliższej wartości typu float. Konwersja danych typu decimal na float lub double powoduje zaokrąglenie danych · typu decimal do typu wyniku (float lub double). Konwersja z typów int, uint lub long do typu float może spowodować utratę dokład- · ności, ale nigdy rzędu wielkości. Konwersja z typu long na double może spowodować utratę dokładności, ale nigdy rzędu · wielkości. Zobacz również Tematy Checked Keyword oraz Checked and Unchecked w dokumentacji MSDN. · 42 | Rozdział 1. Liczby i typy wyliczeniowe 1.14. Wyznaczanie długości dowolnego z trzech boków trójkąta prostokątnego Problem Trzeba obliczyć długość jednego z boków trójkąta w przypadku, gdy jest znana długość dwóch boków bądź jeden z kątów i długość jednego boku. Rozwiązanie Aby znaleźć długość boku, można skorzystać z metod Math.Sin, Math.Cos i Math.Tan klasy Math. Oto równania, w których wykorzystamy wymienione wyżej metody: double theta = 30; double hypotenuse = 5; double oppositeSide = 0; double adjacentSide = 0; oppositeSide = Math.Sin(theta) * hypotenuse; oppositeSide = Math.Tan(theta) * adjacentSide; adjacentSide = Math.Cos(theta) * hypotenuse; adjacentSide = oppositeSide / Math.Tan(theta); hypotenuse = adjacentSide / Math.Sin(theta); hypotenuse = adjacentSide / Math.Cos(theta); W powyższych wzorach theta oznacza znany kąt, zmienna oppositeSide ma wartość równą przyprostokątnej przeciwległej do kąta theta, natomiast zmienna adjacentSide odpowiada długości boku przyległego do kąta theta. Zmienna hypotenuse odpowiada długości przeciw- prostokątnej trójkąta (rysunek 1.1). Rysunek 1.1. Trójkąt prostokątny Oprócz wymienionych trzech statycznych metod długość przeciwprostokątnej trójkąta pro- stokątnego można obliczyć z twierdzenia Pitagorasa. Zgodnie z nim długość przeciwprosto- kątnej trójkąta prostokątnego jest równa pierwiastkowi kwadratowemu z sumy kwadratów pozostałych dwóch boków. Powyższe równanie można rozwiązać za pomocą metody statycznej Math.Sqrt, w następujący sposób: double hypotenuse = Math.Sqrt((xSide * xSide) + (ySide * ySide)); gdzie xSide i ySide są długościami przyprostokątnych trójkąta. 1.14. Wyznaczanie długości dowolnego z trzech boków trójkąta prostokątnego | 43 Analiza Obliczenie długości boku trójkąta prostokątnego jest proste w przypadku, gdy znany jest jeden kąt oraz długość dowolnego boku. Wykorzystując funkcje trygonometryczne: sinus, kosinus i tangens, można wyznaczyć długości nieznanych boków. Oto równania dla sinusa, kosinusa i tangensa: sin(theta) = oppositeSide / hypotenuseSide cos(theta) = adjacentSide / hypotenuseSide tan(theta) = oppositeSide / adjacentSide gdzie theta oznacza wartość znanego kąta. Po przekształceniu powyższych równań otrzy- mujemy: oppositeSide = Math.Sin(theta) * hypotenuse; oppositeSide = Math.Tan(theta) * adjacentSide; adjacentSide = Math.Cos(theta) * hypotenuse; adjacentSide = oppositeSide / tan(theta); hypotenuse = adjacentSide / sin(theta); hypotenuse = adjacentSide / cos(theta); Powyższe równania umożliwiają wyznaczenie długości dowolnego z boków trójkąta dwoma metodami. Jeśli nie znamy żadnego kąta, ale znamy długości dwóch boków, możemy skorzystać z twier- dzenia Pitagorasa w celu wyznaczenia długości pozostałego boku. Twierdzenie Pitagorasa można opisać za pomocą następującego równania: Math.Sqrt(hypotenuse * hypotenuse) = Math.Sqrt((xSide * xSide) + (ySide * ySide)) Po uproszczeniu tego równania do postaci składni języka C#, uzyskujemy następujący kod: double hypotenuse = math.Sqrt((xSide * xSide) + (ySide * ySide)); gdzie zmienna hypotenuse ma wartość odpowiadającą długości przeciwprostokątnej, natomiast zmienne xSide i ySide odpowiadają długościom przyprostokątnych. Zobacz również Temat Math.Class w dokumentacji MSDN. · 1.15. Obliczanie kątów trójkąta prostokątnego Problem Trzeba obliczyć kąty trójkąta prostokątnego w sytuacji, gdy znane są długości dwóch boków. Rozwiązanie Można skorzystać ze statycznych metod Math.Atan, Math.Acos lub Math.Asin klasy Math. Kąt theta wyrażony w radianach można uzyskać za pomocą następującego kodu: 44 | Rozdział 1. Liczby i typy wyliczeniowe d
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

C#. Receptury. Wydanie II
Autor:
,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: