Wstęp
Posługiwanie się aplikacjami .NET Framework firmy Microsoft w C# bardziej zależy od możli
wości funkcjonalnego wykorzystania biblioteki klas .NET Framework, niż od znajomości jerzyka
C#. Podręcznik C#: Księga przykładów wykorzystuje zasoby biblioteki klas .NET Framework
i dostarcza określone rozwiązania, dotyczące tak podstawowych, jak i bardziej złożonych prob
lemów programistycznych. Każde rozwiązanie (tutaj zwane przepisem) zostało przedstawione
w formie opisu, zilustrowanego przykładami odpowiedniego kodu programu.
Intencją podręcznika C#: Księga przykładów nie jest nauczanie programowania ani języka
C#, ale jeśli czytelnik ma podstawowe doświadczenie w programowaniu aplikacji w środowisku
.NET Framework przy użyciu jerzyka C#, niniejsza książka powinna mu posłużyć jako nieoce
nione źródło informacji.
Można w niej znaleźć gotowe rozwiązania problemów lub odpowiednie do tego wskazówki.
C#: Księgaprzykładów może również służyć jako uzupełniające źródło wiedzy o bibliotece klas
.NET Framework. Trudno stać się specjalistą od jerzyka C# i biblioteki klas .NET Framework
wyłącznie na podstawie lektury: konieczne jest więc eksperymentowanie w praktyce i pisanie
coraz co nowych, kolejnych programów. Struktura i zawartość niniejszej książki, jak również
osadzenie rozwiązań w czasie rzeczywistym, oferują dobrą pozycję startową dla rozpoczęcia
własnych prób.
Uwaga Cały zamieszczony w niniejszej książce kod został napisany i przetestowany
w .NET Framework wersji 1.1. W wielu przypadkach przykładowe kody działają również
w wersji 1.0, ale nie można udzielić gwarancji, gdyż nie zostały przetestowane.
Próbki kodu
Sekwencje kodu dla wszystkich przepisów w C#: Księga przykładów są dostępne online, pod
adresem http:l/microsoft.comlmspmslbooks/6456.asp. Aby pobrać przykładowe pliki, należy klik
nąć łącze Companion Content w menu More Information, znajdującym się w prawej części
ekranu. Nastąpi załadowanie strony Companion Content, zawierającej odsyłacz umożliwiający
ściągnięcie tych plików. Aby zainstalować przykładowe pliki, należy kliknąć odsyłacz Download
The Book's Sample Filcs i wykonać instrukcje w programie ustawień. Odsyłacz do przykłado
wego kodu zostanie dołączony do menu Start.
Kod jest zapisany jako zestaw rozwiązań i projektów Visual Studio .NET 2003, podzie
lony na rozdziały i numery przepisów. Każdy rozdział stanowi oddzielne rozwiązanie, a prze
pis - oddzielny projekt, w obrębie rozwiązania dla danego rozdziału. Niektóre z przepisów
w rozdziałach 11 i 12, demonstrujących programowanie sieciowe, zawierają oddzielne projekty
składowe dla klienta i serwera.
x C# - Księga przykładów
Chociaż wszystkie przykłady są napisane jako projekty Visual Studio .NET, większość z nich
zawiera pojedynczy plik źródłowy, który można skompilować i uruchomić niezależnie od Visual
Studio .NET. Jeśli system Visual Studio .NET 2003 nie jest używany, można zlokalizować kod
dla danego przepisu, dzięki poruszaniu się po strukturze katalogowej kodu z przykładu; np. aby
znaleźć kod przepisu 4.3, należy przejrzeć katalog „Chapter04\Recipe04-03". Przy używaniu
kompilatora wywoływanego z linii poleceń, należy pamiętać o włączeniu odniesień do wszyst
kich wymaganych asemblacji biblioteki klas .NET.
Niektóre przykładowe aplikacje wymagają dołączenia argumentów z linii poleceń. Zostaną
one opisane w dalszej części przepisu. Przy użyciu Visual Studio .NET można wprowadzić
te argumenty do właściwości projektu (pod węzłem debugowania - debugging - pozycji Con
figuration Properties). Aby wejść w katalog lub wykorzystać nazwy plików zawierające spacje,
należy całą nazwę ująć w cudzysłów.
Do zainstalowania dwóch wirtualnych katalogów, użytych w przykładach dla rozdziałów
7 oraz 1 2, potrzebne są pewne dodatkowe kroki. Zostały one opisane w pliku tekstowym
readrne.txt na stronie internetowej umożliwiającej ściągnięcie kodów źródłowych.
Wymagania systemu
Aby uruchomić kod przykładów, należy dysponować następujacyrn oprogramowaniem:
• Microsoft .NET Framework SOK, wersji 1.1
• Microsoft Visual Studio .NET 2003 (zalecany)
• Microsoft Windows 2000, Windows XP lub Microsoft Windows Server 2003
• Microsoft SQL Server 2000 lub MSDE - dla przepisów w rozdziale 1O
• Microsoft Internet Information Services (IIS) - dla niektórych przepisów w rozdziałach
7 i 12
Minimalna konfiguracja sprzętu: procesor klasy Pentium II 450 MHz, co najmniej 128 MB
pamięci RAM przy pracy w systemie Microsoft Windows 2000 lub 256 MB przy pracy w syste
mie Windows XP, Windows 2000 Server albo Windows Server 2003, przestrzeń dyskowa około
5 GB w celu zainstalowania Visual Studio .NET 2003. Wymienione parametry stanowią mini
mum, jednak zwiększenie dostępnej pojemności dysku znacznie ułatwi projektowanie.
Uwaga Wprawdzie treścią książki C#: Księga przykładów jest implementacja platformy
.NET Framework dla systemu Windows, równie ważne jest dostarczenie wszystkim pro
gramistom C# niezbędnych zasobów informacji - niezależnie od wykorzystywanej przez
nich platformy projektu i narzędzi. Większość umieszczonych tutaj przykładów działa
we wszystkich implementacjach .NET; wyjątek stanowią te tematy, które nie zostały wdro
żone na wszystkich platformach .NET, takich jak Windows Forms, ADO.NET i ASP.NET.
1
Tworzenie aplikacji
Ten rozdział przybliża podstawowe czynności, niezbędne przy tworzeniu własnych programów
napisanych w języku C#. Znajdują się tu przepisy dotyczące takich czynności, jak:
• Budowa aplikacji trybu tekstowego oraz aplikacji postaci formularza Windows (przepisy
1.1 i 1.2).
• Tworzenie i użytkowanie modułów programowych i bibliotek (przepisy 1.3 i 1.4).
• Dostęp z aplikacji do argumentów linii poleceń (przepis 1.5).
• Wykorzystanie dyrektyw i atrybutów kompilatora do selektywnego włączania kodu podczas
budowy (przepis 1.6).
• Dostęp do elementów programu, ucworwnych w innych językach, których nazwy kolidują
ze słowami kluczowymi języka C# (przepis 1.7).
• Nadanie asembłacjom silnych nazw i weryfikacja asemblacji o silnej nazwie (przepisy 1.8,
1.9, 1.10, i 1.11).
• Podpisywanie asembłacji podpisem cyfrowym Microsoft Authenticode (przepisy 1.12
i 1.13).
• Zarządzanie asemblacjami o dzielonym dostępie, przechowywanymi w globalny buforze
asemblacji (GAC- Global Assembly Cache) (przepis 1.14).
• Ochrona przed osobami mogącymi zdekompilować asembłację (przepis 1. 15).
Uwaga Wszystkie narzędzia dyskutowane w niniejszym rozdziale są dostarczane
łącznie z platformą Microsoft .NET Framework lub .NET Framework SDK. Narzędzia
stanowiące część Framework znajdują się w głównym katalogu wersji Framework prze
znaczonej dla czytelników niniejszej książki. Na przykład: po zainstalowaniu wersji .NET
Framework 1 .1 w lokalizacji domyślnej, znajdą się w katalogu C:\WINDOWS\Microsoft.
NEnFramework\v1 .1 .4322. Proces instalacyjny .NET doda automatycznie ten katalog
do ścieżki środowiska użytkownika.
Ciqgdalszy na nastrpnejstronie
2 C# - Księga przykładów
Cil/gdalszyzpoprzedniejstrony
Narzędzia dostarczone z platformą SOK znajdują się w podkatalogu Bin - wewnątrz
katalogu, w którym zostanie później zainstalowana platforma SOK. Ten katalog nie jest
automatycznie dodawany do ścieżki dostępu; należy ręcznie edytować ścieżkę, aby mieć
wygodny dostęp do narzędzi.
Większość narzędzi umożliwia tworzenie zarówno długich, jak i skróconych formatów
dla przełączników w linii poleceń, kontrolujących ich funkcjonalność. Niniejszy rozdział
zawsze podaje długi format, który wymaga wprawdzie więcej pisania, ale niesie również
więcej informacji. Skrócony format każdego przełącznika można znaleźć w dokumentacji
narzędziowej w .NET Framework SOK.
1 .1 Tworzenie aplikacji trybu tekstowego
Problem
Chcesz zbudować aplikację, która umożliwia użytkownikowi wprowadzanie danych, ale nie
wymaga graficznego interfejsu użytkownika Windows (GUI), a na tekstowej konsoli prezentuje
wyniki działania i wyświetla znak zachęty.
Rozwiązanie
Użyj w swojej implementacji metody static o nazwie Main z jednym z poniższych wpisów
w co najmniej jednym z twoich plików kodów źródłowych.
public static void Main ( ) ;
public static void Main ( st ring [ ] args ) ;
public static int Main ( ) ;
public static int Main ( st ring ( ] a rgs ) ;
Podczas kompilacji należy użyć przełącznika ltarget:exekompilatora C# (csc.exe).
Omówienie
Domyślnie kompilator C# tworzy aplikację trybu tekstowego (Consokapplication), nie jest więc
konieczny przełącznik ltarget:exe. Użycie go może być przydame, jeśli skrypty służące do budowy
aplikacji mają być wykorzystywane wielokrotnie. Podane niżej przykłady przedstawiają klasę
o nazwie ConsokUtils (zasoby konsoli), zdefiniowaną w pliku o nazwie ConsoleUtils.cs:
using System ;
public class ConsoleUtil s {
li Metoda wyświetlenia znaku zachęty i odczytu odpowiedzi z konsoli
public static st ring ReadSt ring ( st ring msg ) {
Console.Write (msg ) ;
}
retu rn System . Console. Readline() ;
}
Rozdział 1 : Tworzenie aplikacji 3
li Metoda wyświetlenia komunikatu w konsoli
public static void WriteSt ring(st ring msg ) {
System . Console . Writeline(msg ) ;
}
li Główna metoda używana do testowania metod ConsoleUtility
public static void Main() {
}
st ring name = ReadSt ring(" Please enter you r name : " ) ;
li Powitanie czytelnika
WriteSt ring("Welcome to the C# P rog ramme r ' s Cookbook, " + name ) ;
I
Aby wbudować klasę ConsokUtils do aplikacji konsolowej o nazwie ConsoleUtils.exe, należy
użyć polecenia csc /target:exe ConsoleUtils.cs. Wynikową asemblację wykonywalną można
uruchomić bezpośrednio z linii poleceń. Metoda Main aplikacji ConsoleUtiłs.exe poprosi użyt
kownika o wprowadzenie imienia, a następnie powita, jak pokazano niżej.
Please enter you r name : Rupert
Welcome to the C# Prog rammer's Cookbook , Rupe rt
W rzeczywistości aplikacje rzadko składają się z pojedynczych plików źródłowych. Na przykład
przytoczona tutaj klasa HelloWorld wykorzystuje klasę ConsokUtils do wyświetlenia polecenia
„Hdlo, world" na konsoli. (HelloWorldjest przechowywane w pliku HelloWorld.es).
public class HelloWo rld {
public static void Main() {
ConsoleUtils . WriteSt ring("Hello , wo rld" ) ;
}
}
Aby utworzyć aplikację konsolową złożoną z co najmniej dwóch plików kodu źródłowego,
należy określić wszystkie pliki źródłowe jako argumenty kompilatora. Na przykład przytoczone
niżej polecenie utworzy aplikację o nazwie MyFirstApp.exe z plików źródłowych HelloWorld.es
i ConsoleUtils.cs.
csc /ta rget : exe /main : HelloWo rld
/ou t : MyFirstApp. exe HelloWo rld . cs ConsoleUtils . cs
Przełącznik /out pozwala określić nazwę skompilowanej asemblacji. Jeśli nie wstanie użyty,
będzie nadana nazwa pliku zapisanego jako pierwszy - w danym przypadku HelloWorld.es.
Ponieważ obie klasy: HelloWorld i ConsokUtils zawierają metody Main, kompilator nie może
automatycznie określić, która metoda reprezentuje właściwy punkt wejściowy (entty point)
asemblacji. Należy użyć przełącznika kompilatora /main, aby zidentyfikować nazwę klasy zawie
rającej właściwy dla aplikacji punkt wejściowy (entrypoint).
4 C# - Księga przykładów
1 .2 Utworzenie aplikacji Windows
Problem
Chcesz utworzyć aplikację wykorzystującą GUI (Graphical User Interface - graficzny interfejs
użytkownika) w oparciu o formularze Windows.
Rozwiązanie
Zastosuj metodę statyczną o nazwie Main w swoim pliku kodu źródłowego. Utwórz w metodzie
Main instancję klasy, rozszerzającej klasę System. Windows.Forms.Form Qest to główny formu
larz twojej aplikacji). Przekaż ten formularz jako obiekt do metody static o nazwie Run klasy
System. Windows.Forms.Application. Wykorzystaj przełącznik /target:winexe dla kompilatora C#
(csc.exe) przy kompilacji swojej asemblacji.
Omówienie
Między budową aplikacji z prostym interfejsem GUI dla systemu Windows a utworzeniem
w pełni ukształtowanej aplikacji w oparciu o cen system istnieje ogromna różnica. Są jednak
czynności, które trzeba wykonać bez względu na to, czy się pisze odpowiednik „Hello World"
systemu Windows, czy opracowanie następnej wersji systemu Microsoft Word. Chodzi tu mię
dzy innymi o następujące czynności:
• Dla każdego formularza wymaganego w aplikacji należy utworzyć klasę, która obejmuje
również klasę System. Windows.Forms.Form.
• W każdej z klas formularzy należy zadeklarować pola reprezentujące kontrolki, które
w danym formularzu wystąpią, np. przyciski, etykiety, listy i ramki tekstowe. Te pola
powinny być zadeklarowane jako prywame (private} łub co najmniej chronione (protected},
aby inne elementy programu nie miały do nich bezpośredniego dostępu. Dla ujawnienia
metod lub właściwości kontrolek należy zaimplementować niezbędne pola w klasie formu
larza, umożliwiając niebezpośredni, kontrolowany dostęp.
• W klasie formularza należy zadeklarować metody zarządzające zdarzeniami wywoływa
nymi przez kontrolki, takimi jak kliknięcie przycisku myszy lub wciśnięcie klawisza, kiedy
aktywnym elementem sterującym jest ramka tekstowa. Te metody powinny być prywatne
(private) lub chronione (protected) i stosować się do standardu .NET eventpattern (wzór
zdarzenia), opisanego w przepisie I6.10. Dotyczy to metod, w których wstanie zdefinio
wana funkcjonalność aplikacji (lub metod przez nie wywoływanych).
• Należy zadeklarować konstruktora dla klasy formularza, który nada instancje wszystkim
kontrolkom formularza i skonfiguruje ich stan początkowy (rozmiar, kolor, pozycję, zawar
tość itd.). Konstruktor powinien także przypisać odpowiednie metody programu obsługi
zdarzeń danej klasy do zdarzeń poszczególnych kontrolek.
• Należy zadeklarować metodę statyczną (static) o nazwie Main - zazwyczaj w postaci pola
klasy głównego formularza danej aplikacji. Ta metoda stanowi punkt wejściowy (entrypoint)
aplikacji i jako taka może posiadać re same podpisy, co metody wymienione w przepisie I. I.
Rozdział 1 : Tworzenie aplikacji 5
W metodzie Main należy utworzyć instancję głównego formularza aplikacji i przekazać
ją jako argument do metody static Application.Run. To spowoduje wizualizację głównego
formularza i uruchomi dla bieżącego wątku standardową pętlę poleceń Windows, w wyniku
czego możliwe będzie przeniesienie danych wejściowych użytkownika (typu naciśnięcia kla
wisza, kliknięcia myszą itp.) do utworzonej aplikacji w charakterze zdarzeń.
Klasa WelcomeForm, przedstawiona w poniższym fragmencie programu, stanowi prostą aplikację
formularzy Windows (Windows Forms), demonstrując wymienione wyżej techniki. W trakcie
działania program monituje użytkownika, żeby wprowadził imię, a następnie wyświetla okno
poleceń z powitaniem w programie C#: Ksirgaprzykładów.
using System . Windows . Fo rms ;
public class WelcomeForm : Form {
li Prywatni członkowie p rzechowuj ący odnośniki do kontrolek formula rza .
p rivate Label label l ;
p rivate TextBox textBox l ;
p rivate Button buttonl ;
l i Konst ruktor służący d o tworzenia instancj i fo rmula rza i konfigu rowania
li kont rolek.
public WelcomeFo rm ( ) {
li Utworzenie instancj i kont rolek użytych w formula rzu .
this . labell = new Label ( ) ;
this . textBoxl = new TextBox ( ) ;
this . buttonl = new Button ( ) ;
li Zawieszenie logiki układu formula rza na czas konfigu rowania
li kont rolek .
this . SuspendLayout ( ) ;
li Konfigu rowanie label l, wyświetlaj ącej zachętę dla użytkownika .
this . labell . Location = new System . D rawing . Point ( 16 , 36 ) ;
this . label l . Name = " labell" ;
this . labell . Size = new System . D rawing . Size ( 128 , 16) ;
this . labell . Tab!ndex = 0 ;
this . labell . Text = " Please ente r you r name : " ;
li Konfigu rowanie textBoxl, odbie raj ącego dane użytkownika .
this . textBoxl . Location = new System . Drawing . Point ( 152 , 32 ) ;
this . textBoxl . Name = "textBoxl" ;
this . textBoxl . Tabindex = 1 ;
this . textBoxl . Text = " " ;
li konfigu rowanie buttonl , klikanego p rzez użytkownika .
this . buttonl . Location = new System . Drawing . Point ( 109 , 89) ;
this . buttonl . Name = " buttonl" ;
this . buttonl . Tabindex = 2 ;
this . buttonl . Text = "Ente r" ;
this . buttonl . Click += new System . EventHandle r ( this . buttonl_Click) ;
6 C# - Księga przykładów
}
}
li Konfigu rowanie formula rza WelcomeForm i dodanie kont rolek .
this . ClientSize = new System . D rawing . Size ( 292 , 126 ) ;
this . Cont rols . Add ( this . buttonl ) ;
this . Cont rols . Add ( this . textBoxl ) ;
this . Cont rols . Add ( this . label l ) ;
this . Name = "forml " ;
this . Text = "C# Prog ramme r ' s Cookbook" ;
li Przywrócenie logiki układu dla formula rza
this . ResumeLayout ( false ) ;
li Punkt wej ścia aplikacj i , two rzy instancję formularza i u ruchamia
li standa rdową pętlę w bieżącym wątku .
public static void Main ( ) {
Application . Run( new WelcomeForm( ) ) ;
}
li Obsługa zda rzeń wywołana po kliknięciu p rzycisku Ente r .
private void buttonl_Click (obj ect sende r, System . EventArgs e) {
li Wypisanie komunikatu debugowania na konsoli
System . Console . WriteLine ( " User ente red : "
+ textBoxl . Text ) ;
li Wyświetlenie powitania j ako okna komunikatu
MessageBox . Show("Welcome to the C# Prog ramme r's Cookbook , "
+ textBoxl . Text , "C# Prog rammer ' s Cookbook" ) ;
}
Aby wbudować klasę WelcomeForm (przechowywaną w pliku o nazwie WelcomeForm.cs)
w aplikację, należy wykorzystać polecenie csc /target:winexe WełcomeFonn.cs. Przełącznik
ltarget:winexe poinformuje kompilator, że aplikacja jest uruchamiana w oparciu o system
Windows. W rezultacie kompilator utworzy moduł wykonywalny w taki sposób, żeby w ogóle
nie angażować konsoli podczas działania aplikacji. Jeśli do budowy aplikacji typu formularz
Windows zostanie użyty przełącznik ltarget:exe zamiast ltarget:winexe, będzie ona również dzia
łała prawidłowo, ale dodatkowo będzie widoczne okno konsoli. Przy produkcji oprogramowa
nia finalnego jest to niepożądane, jednak okno konsoli jest pomocne przy pisaniu procedur
usuwania błędów, rejestrowania podczas rozbudowy i testowania aplikacji Windows Forms.
Można również pisać na konsoli, używając metod Write i WriteLine klasy System.Consol.e.
Rysunek 1-1 przedstawia aplikację WelcomeForm.exe, wykonującą pozdrowienie użytkow
nika o imieniu Rupert. Ta wersja aplikacji została utworzona przy użyciu przełącznika kompi
latora ltarget:exe, dzięki czemu powstało widoczne okno konsoli, w którym można obserwować
dane wyjściowe instrukcji Consol.e. WriteLine programu obsługi zdarzeń button]_Click.
Rozdział 1 : Tworzenie aplikacji 7
Rysunek 1-1 Prosta aplikacja Formularzy Windows (Windows Forms) .
Uwaga Budowa dużych aplikacji w oparciu o GUI jest zajęciem czasochłonnym, które
wymaga prawidłowegonadawania instancji, jak również konfigurowania i wiązania ze sobą
wielu formularzy i kontrolek. Visual Studio .NET firmy Microsoft umożliwia automatyzację
wielu prac związanych z budowaniem aplikacji graficznych . Tworzenie dużych aplikacji
graficznych bez pomocy takich narzędzi, jak Visual Studio .NET, zajmuje o wiele więcej
czasu, jest trudniejsze i zwiększa prawdopodobieństwo pojawienia się błędów w kodzie
wynikowym .
1 .3 Utworzenie i wykorzystanie modułu kodu
Problem
Chciałbyś uzyskać jedną lub więcej z poniższych opcji:
• Poprawić sprawność aplikacji i wykorzystanie przez nią pamięci poprzez ładowanie przez
moduł runtime rzadko używanych typów tylko wtedy, gdy zachodzi rzeczywista potrzeba
ich obecności.
• Skompilować typy zapisane w języku C# do formularza, umożliwiającego wbudowanie
go do aplikacji napisanych w innych językach .NET.
• Wykorzystać typy zaprojektowane w innych językach do potrzeb aplikacji w języku C#.
Rozwiązanie
Zbuduj swój kod źródłowy w języku C# w postaci modułu przy użyciu przełącznika kom
płiatora ltarget:module. Aby włączyć istniejące moduły do swego programu, użyj przełącznika
kompilatora laddmodule.
8 C# - Księga przykładów
Omówienie
Moduły stanowią elementy budowy asemblacji .NET. Każdy z nich składa się z pojedynczego
pliku, który zawiera wyszczególnione niżej elementy:
• Kod Microsoft lntermediate Language (MSIL), utworzony z kodu źródłowego w języku C#
podczas kompilacji.
• Metadane, opisujące typy zawarte w module.
• Zasoby, takie jak ikony i tablice łańcuchów, używane przez cypy w module.
Asemblacje składają się z jednego lub więcej modułów oraz z manifestu asemblacji. W przy
padku pojedynczego modułu, moduł i manifest asemblacji są zazwyczaj dla wygody umiesz
czone w pojedynczym pliku. Gdy modułów jest więcej, asemblacja stanowi logiczne połączenie
więcej nii jednego pliku, które trzeba rozpowszechaniać jako kompletną jednostkę. W tej
sytuacji manifest asemblacji zostanie umieszczony w osobnym pliku albo wbudowany w jeden
z modułów.
Tworzenie asemblacji z wielu modułów sprawia, ie zarządzanie staje się bardziej skompli
kowane, a jej rozpowszechnianie trudniejsze, jednak w pewnych warunkach moduły oferują
znaczne korzyści, takie jak:
• Moduł zostanie załadowany przez moduł runtime cylko wtedy, gdy potrzebne są typy zde
finiowane w cym module. Jeśli więc w grę wchodzi zestaw typów rzadko uiywanych przez
aplikację, moina umieścić je w osobnym module, który zostanie załadowany cylko w razie
konieczności. To rozwiązanie daje następujące korzyści:
O Zwiększa funkcjonalność aplikacji, szczególnie przy pracy w sieci.
O Pozwala na minimalizację wykorzystania pamięci.
• Moiliwość wykorzystania wielu róinych języków przy pisaniu aplikacji, króra działa pod
kontrolą wspólnego modułu runtime CLR (Common Language Runcime), jest wielką zaletą
.NET Framework. Jednakże kompilator C# nie może skompilować kodu w Microsoft Visual
Basic .NET lub COBOL .NET w celu jego dołączenia do asemblacji. Najpierw trzeba
zastosować kompilator określonego języka, żeby przekształcić kod źródłowy w strukturę
MSIL, którą kompilator C# może włączyć do całości, czyli w moduł. Jeśli inni programiści
mają korzystać z tych typów, które zostały utworzone w języku C#, naleiy równiei włączyć
je do modułu.
Aby skompilować plik źródłowy o nazwie ConsoleUtils.cs do postaci modułu, naleiy uiyć pole
cenia csc /target:module ConsoleUtils.cs, w wyniku czego zostanie utworzony plik o nazwie
ConsoleUtils.netmodule. Rozszerzenie netmodule jest domyślne dla wszystkich modułów,
a nazwa pliku taka sama, jak nazwa pliku źródłowego w języku C#.
Moina równiei tworzyć modułyzwielu plikówźródłowych, cospowoduje powstanie pojedyn
czegopliku (modułu), zawierającego MSILi metadane dlawszystkich typów,zawartychwewszyst
kich plikach źródłowych. Polecenie csc /target:module ConsoleUtils.cs WindowsUtils.cs
kompiluje dwa pliki źródłowe o nazwach ConsoleUtils.cs i WindowsUtiłs.cs, aby utworzyć
moduł o nazwie ConsoleUtiłs.netmodule. Moduł ten nosi nazwę pochodną od nazwy pierw
szego wymienionego pliku tekstowego, o ile nie zostanie ona zastąpiona przy użyciu przełącz
nika kompilatora /out. Na przykład polecenie csc /target:moclule /out:Utilities.netmodule
ConsolcUtils.cs WindowsUtils.cs utworzy moduł o nazwie Utilicies.netmodule.
Rozdział 1 : Tworzenie aplikacji 9
Aby utworzyć asemblację składającą się z wielu modułów, należy posłużyć się przełącznikiem
kompilatora laddmodule. Aby utworzyć plik wykonywalny o nazwie MyFirstApp.exe z dwóch
modułów o nazwach WindowsUtils.netmodule i ConsoleUtils.netmodule oraz z dwóch plików
źródłowych o nazwach SourceOne.cs i SourceTwo.cs, należy użyć polecenia csc /out:MyFust
App.exe/target:exe/addmodule:WmdowsUtils.netmodule,ConsoleUtils.netmoduleSoW'CC
One.es SourceTwo.cs. To polecenie utworzy asemblację składającą się z następujących plików:
• MyFirstApp.exe, który zawiera manifest asemblacji oraz MSIL dla typów zadeklarowanych
w plikach źródłowych SourceOne.cs i SourceTwo.cs.
• ConsoleUtils.netmodułe i WindowsUtils.netmodułe, które teraz są integralnymi składni
kami złożonej z wielu plików asemblacji, ale nie zostały zmienione w tym procesie kompila
cji (przy próbie uruchomienia MyFirstApp.exe bez obecności modułów netmodules, system
zgłosi wyjątek System.IO.FileNotFoundException).
1 .4 Utworzenie biblioteki kodów i korzystanie z niej
Problem
Chcesz umieścić zestaw funkcjonalności w bibliotece kodów wielokrotnego użycia, dzięki
czemu różne aplikacje mogą po wywołaniu z niego korzystać.
Rozwiązanie
Aby utworzyć bibliotekę, użyj przełącznika ltarget:library dla kompilatora C# (csc.exe) pod
czas kompilowania asemblacji. Aby się odnieść do biblioteki, użyj przełącznika kompilatora
C# lreference podczas kompilowania aplikacji i określ nazwy żądanych bibliotek.
Omówienie
W przepisie 1.1 pokazano, jak utworzyć aplikację o nazwie FirstApp.exe z dwóch plików
źródłowych: ConsoleUtils.cs i HełloWorld.cs. Plik ConsoleUtils.cs zawiera klasę ConsoleUtils,
która zapewnia metody upraszczające interakcję z konsolą Windows. Gdyby rozszerzyć zakres
funkcjonalności klasy ConsoleUtils, mogłaby ona zawierać funkcjonalność użyteczną dla wielu
aplikacji. Tak więc, zamiast włączać kod źródłowy dla ConsoleUtils do każdej aplikacji, można
go umieścić w bibliotece i wykorzystywać niezależnie.
Aby umieścić w bibliotece plik ConsoleUtils.cs, należy zastosować polecenie csc /target:
library ConsoleUtils.cs. Spowoduje ono utworzenie biblioteki o nazwie ConsoleUtils.dłł. Aby
utworzyć bibliotekę z wielu plików źródłowych, należy zapisać nazwę każdego pliku na końcu
polecenia. Używając przełącznika kompilatora !out, można także określić nazwę biblioteki;
w innym wypadku biblioteka będzie nosiła nazwę pierwszego pliku źródłowego. Na przykład,
by utworzyć bibliotekę o nazwie MyFirstLibrary.dll dwóch plików źródłowych o nazwach
ConsoleUtils.cs i WindowsUtils.cs, należy użyć polecenia csc /out:MyFirstLlbrary.dll /target:
library ConsoleUtils.csWmdowsUtils.cs.
10 C# - Księga przykładów
Przed rozpowszechnieniem biblioteki można zastosować silne nazwy, aby nikt oprócz autora
nie mógł modyfikować asemblacji i przesyłać jej jako własnego oryginału. Silne nazewnictwo
biblioteki pozwala także na instalowanie jej w globalnym buforze asemblacji (GAC - Global
Assembly Cache), co ułatwia wielokrotne wykorzystywanie (w przepisie 1 .9 podano sposób
zastosowania silnego nazewnictwa w asemblacji, a w przepisie 1 .14 - metodę instalacji asembla
cji o silnej nazwie w GAC). Można również podpisać bibliotekę podpisem elektronicznym Aur
henticode; będzie to udokumentowaniem faktu, kro jest wydawcą danej asemblacji - metody
podpisywania asemblacji przy pomocy Authenticode zawiera przepis 1 .1 2.
Wceluskompilowania asemblacji zależnej od typów zadeklarowanych w bibliotekach zewnętrz
nych, trzeba poinformować kompilator, o które biblioteki chodzi - używając przdącznika kom
pilatora !reference. Na przykład, aby skompilować plik źródłowy HelloWorld.es z przepisu 1 .1 ,
gdy klasa ConsoleUtils znajduje się w bibliotece ConsoleUtils.dll, należy zastosować polecenie csc
/referenc.e:ConsoleUtils.dll HelloWorld.es. Nasuwają się trzy uwagi godne zapamiętania:
• Odwołując się do więcej niż jednej biblioteki, należy rozdzielić ich nazwy przecinkiem łub
średnikiem, ale nie spacją. Na przykład: /referenc.e:ConsoleUtils.dll,WindowsUtils.dll.
• Jeżeli kody źródłowe bibliotek nie znajdują się w rym samym katalogu, należy użyć prze
łącznika kompilatora !lib dla określenia dodatkowych katalogów, które kompilator ma
przeszukać w celu odnalezienia bibliotek. Na przykład: /lib:c:\CommonLibraries, c:\Dev\
ThirdPartyLibs.
• Jeżeli odniesienie dotyczy biblioteki, która stanowi asemblację wielu plików, należy sięgnąć
do tego pliku, który zawiera manifest asemblacji. Informacje na temat asemblacji wielu
plików podaje przepis 1 .3.
1 .5 Dostęp do argumentów linii poleceń
Problem
Chcesz mieć dostęp do argumentów określonych w linii poleceń w trakcie wykonywania
aplikacji.
Rozwiązanie
Użyj podpisu do metody Main, co pozwoli ułożyć argumenty w linii poleceń w formie tablicy
łańcuchów. Alternatywnie możesz sięgnąć do argumentów w linii poleceń z dowolnego miejsca
kodu przy użyciu pól typu staticklasy System.Environments.
Omówienie
Zadeklarowanie metody Main aplikacji z jednym z poniższych podpisów umożliwia dostęp
do argumentów linii poleceń w postaci tablicy łańcuchów.
public static void Main ( st ring ( J a rg s ) {}
public static int Main ( st ring [ ) a rg s ) {}
Rozdział 1 : Tworzenie aplikacji 1 1
Podczas wykonywania programu, argument args b�zie zawierał łańcuch (string) dla każdej war
tości wprowadzonej do linii poleceń po nazwie aplikacji. Metoda Main w poniższym przykładzie
bada każdy przekazany argument linii poleceń i wyświetla je na konsoli.
public class CmdLineArgExample {
public static void Main ( st ring [ ] a rgs ) {
li Kolej ne wykonywanie a rgumentów wie rsza polecenia
foreach ( st ring s in a rgs ) {
System . Console . WriteLine ( s ) ;
}
}
}
Ważne jest zrozwnienie sposobu przekazywania argumentów do aplikacji. Jeśli wykonywany
jest przykład CmdlineArgExamp/.e przy użyciu następującego polecenia:
CmdLineArgExample "one \ " two\" th ree" four ' five
to aplikacja wygeneruje na konsoli następujące dane wyjściowe:
one " two"
fou r
' five
six '
three
six '
Należy zauważyć, że inaczej niż w języku C lub C++, nazwa aplikacji nie jest umieszczona
w tablicy argumentów. Użycie znaku podwójnego cudzysłowu (") sprawia, że jako pojedynczy
argument traktowane jest więcej niż jedno słowo, podczas gdy cudzysłów pojedynczy (') tego
nie powoduje. Można włączyć podwójny cudzysłów do argumentu poprzez ujęcie go lewym
ukośnikiem (\). Wszystkie spacje zostaną z linii poleceń usunięte, o ile nie są ujęte w podwójny
cudzysłów.
Jeśli konieczny jest dostęp do argumentów linii poleceń - w innych miejscach kodu, niż metoda
Main- należy przetworzyć argumentylinii poleceńw swojej metodzie Main i zapamiętać je do póź
niejszego wykorzystania. Alternatywnie można użyć klasy System.Environment, która zawiera dwa
pola static dostarczające informację o linii poleceń: Commandline i GetCommandlineArgs. Właś
ciwość Commandline zwraca łańcuch zawierający całą linię poleceń, która uruchomiła aktualny
proces. W zależności od systemu operacyjnego, w którym działa dana aplikacja, nazwa aplikacji
może być poprzedzona informacją o ścieżce. Systemy Microsoft Windows NT 4.0, Windows 2000
i Windows XP nie dołączają informacji o ścieżce; systemy Windows 98 i Windows ME infor
mację tę dołączają. Metoda GetCommandLineArgs przekazuje zwrotnie tablicę zawierającą argu
menty linii poleceń. Tablica ta może być przetworzona w taki sam sposób, jak tablica przekazana
do metody Main, omówiona na początku tego przepisu. W przeciwieństwie do tablicy przeka
zywanej do metody Main pierwszym dementem w tablicy, przekazanej zwrotnie przez metodę
GetCommandLineArgs, jest nazwa aplikacji.
12 C# - Księga przykładów
1 .6 Selektywne włączanie kodu
podczas jego budowy
Problem
Chcesz selektywnie włączać i wyłączać fragmenty kodu źródłowego ze skompilowanej asemblacji.
Rozwiązanie
Użyj dyrektyw preprocesora #if, #elif, #else i #endifw celu identyfikacji bloków kodu, które mają
być warunkowo dołączane do asemblacji. Wykorzystaj atrybut System.Diagnostics.Conditional
Attributecelem zdefiniowania metod, które mająbyćwywoływane wyłączniewarunkowo. Możesz
kontrolować dołączanie warunkowego kodu dzięki zastosowaniu w kodzie dyrektyw #dejine
i #umie/ lub poprn:z użycie przełącznika !de.fine przy uruchomieniu kompilatora C#.
Omówienie
Jeśli aplikacja ma działać w sposób zależny od takich czynników, jak platforma czy środowisko,
w którym się znajduje, należy wbudować w logikę kodu mechanizmy sprawdzające modułu
runtime, które będą przełączać wykonywanie poszczególnych wariantów. Jednakie takie roz
wiązanie może zaowocować zwięk."7.oną objętością kodu i obniżyć efektywność wykonania,
zwłaszcza gdy chodzi o wicie wariantów lub wicie lokalizacji. Alternatywnym rozwiązaniem
jest utworzenie różnych wersji danej aplikacji, działających na różnych docelowych platformach
i w różnych środowiskach. Taka realizacja nie wyklucza ani zwiększenia objętości kodu, ani
pogorszenia efektywności, może się okazać jednak optymalnym rozwiązaniem, jeśli dla każdej
wersji musi zadziałać inny kod źródłowy. Tak więc język C# umożliwia dostosowanie do potrzeb
klienta wersji aplikacji od podstaw, od pojedynczego kodu.
Dyrektywy preprocesora #if, #elif, #else i #endifumożliwiają identyfikację tych bloków kodu,
które kompilator może włączyć do asemblacji tylko wtedy, gdy podczas kompilacji wstaną
zdefiniowane specjalne symbole. Te symbole funkcjonują jako przełączniki „on/off' i nie posia
dają wartości: symbol jest po prostu zdefiniowany lub nie. Aby zdefiniować symbol, można
użyć albo dyrektywy #de.fine, albo wykorzystać przełącznik kompilatora /de.fine. Symbole zde
finiowane przy pomocy #de.fine pozostają aktywne do końca pliku, w którym je zdefiniowano.
Symbole zdefiniowane przy pomocy przełącznika kompilatora !de.fine są aktywne we wszystkich
plikach źródłowych, które mają być kompilowane. Aby symbol utworzony przy użyciu prze
łącznika kompilatora /de.fine uczynić z powrotem niezdefiniowanym, przewidziana jest w języku
C# dyrektywa #undef, zapewniająca brak zdefiniowania symbolu w określonych plikach źródło
wych. Wszystkie dyrektywy #de.fine i #undefmuszą być umieszczone przed kodem, na początku
pliku źródłowego, a także przed wszystkimi dyrektywami using. Dla symboli rozróżniana jest
wielkość liter.
W poniższym przykładzie kod przypisuje różne wartości lokalnej zmiennej platformName,
zależnie od tego, czy symbole winXP, win2000, winNTlub win98 są zdefiniowane. Nagłówek
kodu definiuje symbole win2000 i release (w tym przykładzie nie wykorzystany) i anuluje defi
nicję symbolu win98 w przypadku, gdy był on zdefiniowany w linii poleceń kompilatora.
Rozdział 1 : Tworzenie aplikacji 13
#define win2000
#define release
#undef win98
using System ;
public class ConditionalExample {
public static void Main ( ) {
}
}
li Deklaracj a łańcucha
st ring platformName ;
#if winXP li Kompilacj a dla Windows XP
platformName = "Mic rosoft Windows XP" ;
#elif win2000 li Kompilacj a dla Windows 2000
platformName = "Mic rosoft Windows 2000 " ;
#elif winNT li Kompilacj a dla Windows NT
platformName = "Mic rosoft Windows NT" ;
#elif win98 li Kompilacj a dla Windows 98
platformName = "Microsoft Windows 98" ;
#else li Nieznana platforma
platfo rmName = " Unknown " ;
#endif
Console . WriteLine ( platformName ) ;
Aby utworzyć klasę ConditionalExample (zawartą w pliku o nazwie ConditionalExample.cs)
i zdefiniować symbole winXP i DEBUG (nie wykorzystany w niniejszym przykładzie), należy
użyć polecenia csc /define:winXP;DEBUG ConditionalExample.cs.
Konstrukcja #if.. #endif pozwoli oszacować zdania #ifi #eliftylko wtedy, gdy stwierdzi,
że jedna z wartości jest prawdziwa. Oznacza to, że jeśli wstanie zdefiniowanych wiele symboli
(na przykład winXP i win2000), ważna jest kolejność ucworwnych zdań. Kompilator dołączy
tylko kod ze zdania, które oceni jako prawdziwe. Jeśli żadne zdanie nie jest prawdziwe, kompi
lator umieści kod w zdaniu #else.
Można również użyć operatorów logicznych, aby oprzeć kompilację warunkową na więcej niż
jednym symbolu. Tabela 1 - 1 podsumowuje operatory posiłkujące się dyrektywą #if..#endif.
Tabela 1-1 Operatory logiczne posiłkujące się dyrektywą #if.. #endif
Operator Przykład Opis
#ifwinXP == true Równość. Przypisuje wartość true, jeśli symbol winXP
jest zdefiniowany. Równoważny z #ifwinXP.
!= #ifwin.XP != true Nierówność. Przypisuje wartość true, jeśli symbol winXP
nie jest zdefiniowany. Równoważny z #if!winXP.
&& #ifwinXP && release Logiczne AND. Przypisuje wartość true, jeśli obydwa
symbole winXP i release są zdefiniowane.
Ciq.gdalszy na nastrpnejstronie
14 C# - Księga przykładów
Tabela 1-1 Operatory logiczne posiłkujące się dyrektywą #if..#endif
Operator Przykład Opis
Ciqgda/szy
zpoprzedniejstrony
li #ifwinXP li release Logiczne OR. Przypisuje wartość true, jeśli któryś
z symboli winXP lub release jest zdefiniowany.
o #if(winXP li win2000)
&& release
Nawiasy umożliwiają grupowanie wyrażeń. Przypisuje
wartość true, jeśli zdefiniowany jest symbol winXPlub
win2000oraz symbol releasejest zdefiniowany.
Ostrzeżenie Należy zwracać uwagę, aby nie nadużywać dyrektyw kompilacji warunko
wej i nie przesadzać ze złożonością wyrażeń warunkowych. W przeciwnym wypadku kod
może się stać nieczytelny i trudny w zarządzaniu, zwłaszcza w wypadku rozbudowy.
Mniej elastyczną, ale bardziej elegancką alternatywą dyrektywy preprocesora #if jest atry
but System.Diagnostics.Conditiona!Attribute. Po zastosowaniu atrybutu Conditiona!Attribute
do metody, kompilator zignoruje wszystkie wywołania tej metody, jeśli symbol określony przez
Conditiona/Attribute nie jest zdefiniowany w punkcie wywołania. W podanym niżej przykładzie
Conditiona/Attributeokreśla, że wywołania metody DumpState powinny być zawarte w skompilo
wanej asemblacji tylko wtedy, kiedysymbol DEBUGzostał zdefiniowany podczas kompilacji.
[ System . Diagnostics . Conditional ( "DEBUG" ) J
public static void DumpState ( ) {//-}
Użycie Conditiona/Attribute centralizuje logikę warunkowej kompilacji w deklaracji metody,
co oznacza możliwość swobodnego umieszczania wywołań do metod warunkowych, bez
potrzeby przepełniania kodu dyrektywami #if. Ponieważ kompilator po prostu usuwa wywo
łania metody warunkowej z kodu, nie może on zawierać zależności od wartości zwracanych
z metody warunkowej. Oznacza to, że Conditiona/Attribute można stosować tylko do metod,
które zwracają stan pusty (void).
Można zastosować wielokrotne instancje Conditiona/Attribute do metody, aby utworzyć
zachowanie logiczne OR. Wywołania podanej niżej wersji metody DumpState będą skompilo
wane tylko wtedy, gdy symbole DEBUG OR TESTsą zdefiniowane.
[ System . Diagnostics . Conditional ( "DEBUG" ) J
[System . Diagnostics . Conditional ( "TEST" ) J
public static void DumpState( ) {//-}
Osiągnięcie zachowania logicznego AND nie jest tak proste i wymaga użycia pośredniej metody
warunkowej, co szybko prowadzi do nakładania się zapisów i w efekcie do złożonego, trudnego
do zrozumienia i utrzymania kodu. Oto szybki przykład, wymagający zdefiniowania symboli
DEBUGoraz TESTdla wywoływania funkcjonalności DumpState (zawartej w DumpState2).
[ System . Diagnostics . Conditional ( " DEBUG" ) J
public static void DumpState ( ) {
DumpState2 { ) ;
}
[System . Diagnostic s . Conditional ( "TEST" ) J
public static void DumpState2 ( ) {li-}
Rozdział 1 : Tworzenie aplikacji 15
Uwaga Klasy Debugi Trace, z przestrzeni nazw System.Diagnostics, używają Conditional
Attributedla różnych metod. Metody klasy Debug są warunkowe na podstawie definicji sym
bolu DEBUG, a metody klasy Trace są warunkowe na podstawie definicji symbolu TRACE.
1 .7 Dostęp do elementu programu o tej
samej nazwie, co słowo kluczowe
Problem
Chcesz mieć dostęp do pola typu, ale nazwa typu lub nazwa pola jest taka sama, jak słowo
kluczowe C# .
Rozwiązanie
Dołącz znak (@) jako prefiks do wszystkich instancji nazwy identyfikatora w swoim kodzie.
Omówienie
Platforma .Net Framework umożliwia użycie składowych oprogramowania, pochodzących
z aplikacji języka C#, napisanych w innych językach .NET. Każdy język zawiera własny zestaw
słów kluczowych (lub słów zarezerwowanych), co nakłada na programistę określone obostrzenia
dotyczące nazw, które może przypisywać elementom programu, takim jak typy, pola i zmienne.
Może się zdarzyć, że programista uruchamiający składową programu w innym języku, użyje
niechcący słowa kluczowego języka C# jako nazwy elementu programu. Symbol @ umożliwia
uniknięcie ewentualnego konfliktu, dotyczącego użycia słowa kluczowego C# jako identyfika
tora. Poniższy fragment kodu nadaje instancję obiektowi typu operator (być może telefoniczny)
i ustawia jego właściwość volatile (ulotność) na wartość true, przy czym zarówno operator jak
volatile są słowami kluczowymi języka C#.
li Tworzenie instancj i obiektu operato ra
@ope rator Operatorl = new @operator( ) ;
li Określanie właściwości volatile dla operatora
Operatorl . @volatile = t rue;
16 C# - Księga przykładów
1 .8 Tworzenie i zarządzanie parami
kluczy silnej nazwy
Problem
Chcesz utworzyć klucze publiczny i prywatny (parę kluczy), aby przy ich pomocy można było
przypisać asemblacjom silne nazwy.
Rozwiązanie
Użyj narzędzia Strong Name (sn.exe) do wygenerowania pary kluczy i zapamiętaj je w pliku lub
w zasobniku kluczy programu usług kryptograficznych CSP (Cryptographic Service Provider).
Uwaga Program CSP jest elementem programu Win32 CryptoAPI, który umożliwia
usługi w zakresie utajniania i odtajniania informacji oraz generowania podpisu cyfrowego.
CSP oferuje również możliwości przechowywania kluczy, przy zastosowaniu mocnych
narzędzi szyfrujących i możliwości zasobów systemu operacyjnego do zabezpieczenia
jego zawartości. Dokładniejsze omówienie CSP i CryptoAPI wykracza poza zakres niniej
szej książki. Po odnośne szczegółowe informacje na temat CryptoAPI należy sięgnąć
do dokumentacji SOK.
Omówienie
Aby wygenerować nową parę kluczy i zapamiętać je w pliku o nazwie MyKeys.snk, należy wyko
nać polecenie sn -k MyKeys.snk (.snk jest zwykle używanym rozszerzeniem dla plików zawiera
jących klucze silnej nazwy). Wygenerowany plik zawiera obydwa klucze: publiczny i prywatny.
Można obejrzeć klucz publiczny przy użyciu polecenia sn -tp MyKeys.snk, które wygeneruje
informację wyjściową, podobną do przedstawionej niżej (w skróconej formie):
Microsoft (R) . NET Framework Strong Name Utility Version 1 . 1 . 4322 . 573
Copyright ( C ) Mic rosoft Corporation 1998- 2002 . All rights reserved .
Public key is
07020000002400005253413200040000010001002b4ef3c2bbd6478802b64d0dd3f2e7c65ee ; <$V
E>6478802b63cb894a782f3aladbb46d3ee5ec5577e7dccc818937e964cbe997c 12076c19f2d7ad
179f15f7dccca6c6b72a
Public key token is 2ald3326445fc02a
Token klucza publicznego, przedstawiony przy końcu wydruku, to co najmniej 8-bajtowy skrót
kryptograficzny (hash), obliczony na podstawie klucza publicznego. Ponieważ klucz publiczny
jest tak długi, moduł .NET wykorzystuje token ze względu na jego spakowaną formę - zarówno
przy wyświetlaniu nazwy, jak i w wypadku odwołań do danego klucza w innych asemblacjach.
Ogólne omówienie skrótów kryptograficznych zawiera rozdział 14.
Spis treści Wstęp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix 1 Tworzenie aplikacji . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1 Tworzenie aplikacji trybu tekstowego................................. .......... 2 1.2 Utworzenie aplikacji Windows ............................................... . 4 1.3 Utworzenie i wykorzystanie modułu kodu ........ . .............................. 7 1.4 Utworzenie biblioteki kodów i korzystanie z niej ................................... 9 1.5 Dostęp do argumentów linii poleceń .......................................... 1O 1.6 Selektywne włączanie kodu podczas jego budowy................................ 12 1.7 Dostęp do elementu programu o tej samej nazwie, co słowo kluczowe ................ 15 1.8 Tworzenie i zarządzanie parami kluczy silnej nazwy .............................. . 16 1.9 Nadawanie asemblacji silnej nazwy..................... . ............ . .. . ..... 18 1.1O Sprawdzenie, czy asemblacja o silnej nazwie nie została zmodyfikowana.............. 19 1.1 1 Opóźnienie podpisu asemblacji ............................................. 20 1.12 Podpisywanie asemblacji podpisem cyfrowym Authenticode ...................... 22 1.13 Utworzenie testowego certyfikatu wydawcy oprogramowania i obdarzenie go zaufaniem.. 25 1.14 Zarządzanie globalnym buforem asemblacji (GAC). .............................. 26 1.15 Ochrona przed dekompilacją kodu przez osoby postronne......................... 27 2 Praca z danymi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.1 Efektywne manipulowanie zawartością łańcuchów ............................... 29 2.2 Przekodowanie łańcucha za pomocą innego schematu kodowania znaków . ............ 3 1 2.3 Konwersja podstawowych typów wartości na tablicę bajtową ....................... 34 2.4 Kodowanie danych binarnych jako tekstu........................... ............ 36 2.5 Testowanie poprawności danych wejściowych przy użyciu wyrażeń regularnych ......... 38 2.6 Wykorzystywanie skompilowanych wyrażeń regularnych.................... . ...... 4 1 2.7 Tworzenie daty i czasu z łańcucha......................... . .................. 43 2.8 Dodawanie, odejmowanie i porównywanie dat i czasów............................ 44 2.9 Sortowanie tablicy lub wykazu tablic ArrayUst................................... 4 6 2.1O Kopiowanie kolekcji do tablicy....... ......................... ......... . ..... 47 2.11 Tworzenie kolekcji elementów jednolitego typu ......._.. .·„ .· . : . . . . . . . . . . . . . . . . . . . „ 48 2.12 Zapamiętywanie serializowanego obiektu w pliku... : ............................ 50 ·. 3 Domeny aplikacji, odbicie i metadane. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . · . . 53 3.1 Tworzenie domeny aplikacji................................................. 53 3.2 Przekazanie obiektów poza granice domeny aplikacji.............................. 55 3.3 Unikanie ładowania zbędnych asemblacji do domen aplikacji........................ 56 3.4 Utworzenie typu, który nie może przekroczyć granic domeny aplikacji ................. 57 3.5 Ładowanie asemblacji do aktualnej domeny aplikacji.............................. 58 3.6 Wykonanie asemblacji w innej domenie aplikacji ................................ . 60 3.7 Utworzenie instancji typu w innej domenie aplikacji ...... ......... . ............... 62 Ili
lv C# - Księga przykładów 3.8 Przekazywanie danych między domenami aplikacji . .............................. 65 3.9 Zwalnianie asemblacji i domen aplikacji . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 3.1O Uzyskiwanie informacji o typie.. ........ . ................................... 6 8 3.11 Testowanie typu obiektu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 3.12 Tworzenie instancji obiektu przy użyciu odbicia ................................. 7 1 3.13 Tworzenie niestandardowego atrybutu........................................ 74 3.14 Sprawdzenie atrybutów elementu programu przy użyciu odbicia .................... 76 4 Wątki, procesy i synchronizacja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 4.1 Wykonanie metody przy użyciu puli wątków . . . . . .. . .. . . . . . . . ... . . . . . . . . . . . . . . . . 79 4.2 Asynchroniczne wykonywanie metody. . . . . . . . . . . . . .. . . . . . . . . . ... . . . . . . . . . . .. . . 82 4.3 Wykonanie metody przy użyciu timera . . ... . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . 89 4.4 Wykonanie metody poprzez sygnalizację obiektu WaitHandle . . . . . . ... . . . . . . . . . . . . . . 9 1 4.5 Wykonanie metody przy użyciu nowego wątku . . . . . . . . ...... . . . . . . . . . . . . . . . . . . . . 9 3 4.6 Nadzór nad wykonaniem wątku . . . . . . . .... . .. . . . . . . . . . . . . . . . . . . . . . . . . . . ... . . . 95 4.7 Rozpoznanie momentu zakończenia wątku... . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . 99 4.8 Synchronizacja działalności wielu wątków . .................................... 100 4.9 Tworzenie instancji kolekcji przystosowanej do działania w trybie wielowątkowym . . . .. . . 104 4.10 Zapoczątkowanie nowego procesu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 4.1 1 Zakończenie procesu . ................................................... 108 4.12 Ograniczenie równoczesności działania do jednej tylko instancji aplikacji ............. 1 1 1 5 Przetwarzanie XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 5.1 Przedstawienie struktury dokumentu XML w postaci drzewa . ...................... 114 5.2 Wstawianie węzłów do dokumentu XML . ..................................... 117 5.3 Szybkie dołączanie węzłów do dokumentu XML ................................ 119 5.4 Odnajdywanie określonych elementów przy pomocy nazwy.. ...................... 12 1 5.5 Odczytywanie węzłów XML w określonej przestrzeni nazw XML. . ................... 12 3 5.6 Odnajdywanie elementów poprzez przeszukiwanie XPath .......................... 124 5.7 Odczyt i zapis XML bez ładowania do pamięci całego dokumentu. . . . .... . . . . . . . . . . . . 127 5.8Sprawdzenie zgodności dokumentu XML ze schematem . ......................... 1 3 0 5.9 Serializacja XML przy pomocy obiektów niestandardowych . ........... . ........... 1 3 5 5.1O Tworzenie schematu dla klasy .NET . ....................................... 1 3 8 5.11 Generowanie klasy ze schematu . .......................................... 1 3 9 5.12 Wykonywanie transformacji XSL . .......................................... 1 3 9 6 Formularze Windows. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 6.1 Programowe dodawanie kontrolki . .......................................... 144 6.2 Przypisanie danych do kontrolki. . . . ...... . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . 145 6.3 Przetwarzanie wszystkich kontrolek formularza . . . . . ... . .. . . . . . . . . . . . . . . . . . . . . . . 147 6.4 Śledzenie widocznych formularzy w aplikacji ................................... 14 8 6.5 Znajdowanie wszystkich formularzy podrzędnych MDI . . . . . . . . . . . . .... . . . · . . . . . . . . . 150 6.6 Zapamiętywanie rozmiaru i położenia formularza.... . . . . . . . .. . . .. . . . . . . . . . . .. . . . 15 1 6.7 Wymuszenie przewijania okna listy . ......................................... 15 3 6 . 8 Ograniczenie zawartości okna tekstowego do wejściowych danych numerycznych . ..... 15 4 6.9 Wykorzystanie okna autouzupelniania typu combo. . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . 15 5
Rozdział v 6.1O Sortowanie zawartości listy według dowolnej kolumny .......................... 158 6.11 Przypisanie menu kontekstowego do kontrolki............... ... .. ..... ........ 160 6.12 Wykorzystywanie części głównego menu do menu kontekstowego . ................ 16 1 6.13 Tworzenie formularza wielojęzycznego..... . .. .... . .......... .. ... .. .. ... . .. . 16 3 6.14 Tworzenie formularzy nieruchomych ...... .................................. 165 6.15 Przekształcenie formularza bez ramki w obiekt ruchomy ................. . ....... 166 6.16 Tworzenie animowanej ikony paska systemowego. ......... ........ ............ 168 6.17 Test kontrolki wejściowej.... . ... .. ... ... ...................... .... .... . . . 169 6.18 Wykonanie operacji przeciągania i upuszczania ....... .... . ..... ............... 17 1 6.19 Używanie pomocy kontekstowej .......................... .... . ... ......... 173 6.20 Zastosowanie stylu Windows XP do kontrolek ....... ....... ........ ........... 174 7 ASP.NET i formularze Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 7.1 Przekierowanie użytkownika na inną stronę .............. ...................... 178 7.2 Zapamiętywanie informacji między zgłoszeniami ...... ... .. . .... . . . ..... .. . ..... 179 7.3 Tworzenie zmiennych członkowskich strony z informacją o stanie . .................. 18 3 7.4 Odpowiedż na zdarzenia po stronie klienta w języku JavaScript ..................... 185 7.5 Pokazanie wyskakującego okna przy użyciu JavaScript . .. . . . .. .. . . ..... . . . .. ... .. 187 7.6 Programowe ustawianie fokusu kontrolki. ........ .... . ..... . . . ..... . ...... . . . . 188 7.7 Umożliwienie załadowania pliku przez użytkownika . ..... ..... . .. ..... . ...... . ... 189 7.8 Wykorzystanie uwierzytelnienia llS .............. ...... ..... .... .. ..... . ... .. 192 7.9 Użycie uwierzytelniania opartego o formularze............ ......... ....... . ..... 196 7.1O Wykonanie selektywnych testów poprawności danych wejściowych ................ 199 7.11 Dynamiczne dołączanie kontrolek do formularza Web .......... ...... .. . .... .. .. 201 7.12 Dynamiczne renderowanie obrazu . .. ............................ ....... .. .. 203 7.13 Programowe ładowanie kontrolek użytkownika................................. 207 7.14 Wykorzystywanie buforowania stron i fragmentów. ......................... .... 2 11 7.15 Powtórne użycie danych z bufora ASP.NET .................... .. ........ ..... 2 12 7.16 Włączanie wykrywania i usuwania błędów (debugging) strony Web .... ...... . . ..... 2 15 7.17 Zmiana zezwoleń dla kodu ASP.NET . ... ........................... ..... .... 2 18 8 Grafika, multimedia I drukowanie . . . . . . . „ . . . • „ . . „ . . „ " „ . . „ . . „ • • • 221 8.1 Odnajdywanie wszystkich zainstalowanych czcionek .. ................... .... .... 222 8.2 Wykonanie testu trafienia dla konturów ..... ..... ................. ........ .... 224 8.3 Utworzenie kontrolki o nieprostokątnym kształcie.... ....... . ...... .. ..... .. . .... 227 8.4 Utwórz ruchomego „duszka" ............................................. .. 229 8.5 Utworzenie obrazu dającego się przewijać.. ..................... .. .... . ..... .. 232 8.6 Wykonanie zrzutu ekranu... ... ..... ....... . . . ..... . . . . ..... . . . ... .. ....... 2 3 3 8.7 Zastosownie podwójnego buforowania w celu przyspieszenia przerysowywania ..... ... 2 35 8.8 Pokazanie miniatur obrazów . .... ....... ... .... ......................... ... 2 38 8.9 Wygenerowanie prostego dżwięku 'beep'..... ........ ...... ........ . ... ... . . . . 239 8.1O Odtworzenie pliku WAV lub MP3 ..................... ...................... 240 8.11 Przedstawienie animacji z DirectShow............. .............. .... ... . ... . 242 8.12 Wyszukiwanie informacji o zainstalowanych drukarkach ...... ................... 245 8.13 Drukowanie prostego dokumentu . .................................... ..... 247 8.14 Drukowanie dokumentu wielostronicowego..... ...... .............. .. . . . .. . . . 249
vl C# - Księga przykładów 8.15 Drukowanie sformatowanego tekstu . ....... . . ....... . .. . ..... ... ........ .. . 252 8.16 Wyświetlenie dynamicznego podglądu wydruku. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 8.17 Zarządzanie zadaniami drukowania . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 9 Pliki, katalogi i operacje wejścia/wyjścia (1/0) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261 9.1 Odczytywanie informacji o pliku lub katalogu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262 9.2 Ustawienie atrybutów pliku i katalogu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265 9.3 Kopiowanie, przenoszenie i usuwanie pliku lub katalogu. . . . . . . . . . . . . . . . . . . . . . . . . . . 266 9.4 Obliczanie rozmiaru katalogu . ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 9.5 Wyszukiwanie informacji o wersji pliku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . 270 9.6 Wyświetlenie drzewa katalogów na bieżąco w kontrolce TreeView . . . . . . . . . . . . . . . . . . . 271 9.7 Odczytywanie i zapisywanie pliku tekstowego . . . . . . . . . . . . . . . . . . . . . . . . . . . . „ . . . . 274 9.8 Odczytywanie i zapisywanie pliku binarnego . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 9.9 Asynchroniczny odczyt pliku . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 9.1o Wyszukanie plików odpowiadających wyrażeniu zawierającemu znaki zastępcze . . . . . . . 280 9.11 Testowanie identyczności dwóch plików . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 1 9.12 Posługiwanie się łańcuchami reprezentującymi nazwy plików. . . . . . . . . . . . . . . . . . . . . . 283 9.13 Określenie, czy ścieżka dotyczy katalogu, czy pliku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284 9.14 Praca ze ścieżkami względnymi. . . . . . . . . . . . . . . . . „ . . . . . . . . . ... . . . . . . . . . . . . . 285 9.15 Utworzenie pliku tymczasowego . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . 286 9.16 Odczytanie wielkości całkowitej wolnej przestrzeni dyskowej . . . . . . . . . . . . . . . . . . . . . . 287 9.17 Wyświetlenie wspólnych plikowych okien dialogowych . . . . . . . . . . . . . . . . . . . . . . . . . . 288 9.18 Używanie izolowanego magazynu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 9.19 Monitorowanie zmian w systemie plików . . . . . . . ... . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 9.20 Dostęp do portu COM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295 1O Dostęp do baz danych . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 10.1 Połączenia z bazą danych . . ...... . . . . . . ..... . . . . . . . .. . . ......... . . . . . . ... 298 10.2 Tworzenie pul połączeń . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . ... . . 300 10.3 Wykonanie polecenia SQLlub procedury składowanej . . .. . . .. . . . . . . . . . . . . . . . . . . . 303 10.4 Wykorzystanie parametrów w poleceniu SQL lub procedurze składowanej . . . . . . . . . . . . 306 10.5 Przetwarzanie wyników zapytania SQL przy użyciu czytnika danych. . .. . . . . . . . . . . . . . 309 10.6 Uzyskanie dokumentu XMLw zapytaniu SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 12 10.7 Wyszukiwanie wszystkich instancji SQL Server 2000 w sieci . . . . . . . . . . . . . . . . . . . . . . 3 15 11 Programowanie sieciowe i międzysieciowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317 11. 1 Pobranie pliku poprzez HTIP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . 3 18 11.2 Pobranie pliku I przetwarzanie go przy użyciu strumienia . . . . . . . . . . . . .. . . . . . . . . . .. 3 19 11.3 Uzyskanie strony HTML z witryny wymagającej uwierzytelnienia . . . . . . . . . . . . . . . . . . . 32 1 11.4 Wyświetlenie strony Web w aplikacji Windows. . . . . . . . . .. . . . . . . . . . .. . . . . . . . . . . . 322 11.5 Uzyskanie adresu IP aktualnego komputera ......... .................. ........ 325 11.6 Uzyskanie nazwy hosta dla adresu IP. . . ... . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . . . 326 11.7 Pingowanie adresu IP . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327 11.8 Komunikacja przy użyciu TCP . . . . . . . . . . . . . . . . . . . . . . . . .. . ... . . . . . . . . . . . . . . . 3 30 11.9 Uzyskanie adresu IP klienta z gniazda połączenia . . . . . . . . ... . . . . . . . . . . . . . .. . . . . . 3 34 11.1O Ustawienie opcji gniazda . . .. . .............. .......... .................. . 3 36
Rozdział vii 1 1.11 Utworzenie wielowątkowego serwera TCP .................................. 3 37 1 1.12 Asynchroniczne używanie TCP ........................................... 3 39 1 1.13 Komunikacja przy użyciu UDP ................. ........................... 342 1 1.14 Wysyłanie wiadomości e-mail poprzez SMTP ................................ 345 11.15 Przesyłanie i odbieranie wiadomości e-mail za pomocą MAPI .................... 346 12 Usługi Web XML I zdalny dostęp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349 12.1 Unikanie kodowania na stałe adresu URL usług XML Web ........................ 350 12.2 Wykorzystanie buforowania odpowiedzi usługi XMLWeb......................... 352 12.3 Buforowanie danych usługi XML Web ....................................... 353 12.4 Utworzenie transakcyjnej metody Web ...................................... 355 12.5 Definiowanie poświadczeń dla usługi XMLWeb................................ 357 12.6 Asynchroniczne wywołanie metody Web..................................... 359 12.7 Przekształcenie obiektu na zdalny .......................................... 36 1 12.8 Rejestracja wszystkich zdalnych klas zdefiniowanych w asemblacji... . ............. 365 12.9 Utrzymywanie zdalnego obiektu w llS ....................................... 367 12.10 Wyzwolenie zdarzenia przez zdalny kanał.......... . ......................... 369 12.1 1 Kontrolowanie czasu życia zdalnego obiektu ............................. . ... 373 12.12 Zarządzanie wersjami zdalnych obiektów.................................. .. 374 12.13 Utworzenie metody jednokierunkowej korzystającej z usług XML Web lub zdalnego dostępu .................................................... . . . . ......... 376 13 Bezpieczeństwo w czasie wykonania. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 13.1 Dopuszczenie częściowo zaufanego kodu do asemblacji o silnych nazwach .......... 380 13.2 Wyłączenie zabezpieczeń dostępu do kodu ................................... 382 13.3 Wyłączenie sprawdzania uprawnień do wykonania.............................. 384 13.4 Zagwarantowanie, że moduł runtime przyzna asemblacji specjalne uprawnienia........ 385 13.5 Ogranicznie uprawnień przyznawanych asemblacji ............................. 387 13.6 Przeglądanie żądań uprawnień wykonywanych przez asemblację................. . . 389 13.7 Określenie posiadania uprawnień podczas wykonywania programu ................. 391 13.8 Ograniczenie prawa do rozszerzenia klasy i nadpisywania jej pól ................... 392 13.9 Przeglądanie ewidencji asemblacji.......................................... 394 13.1O Posługiwanie się ewidencją podczas ładowania asemblacji ................ . ..... 395 13.1 1Modyfikowanie zabezpieczeń czasu wykonania przy użyciu ewidencji domeny aplikacji . 397 13.12 Posługiwanie się zabezpieczeniami czasu wykonania przy użyciu zasad zabezpieczeń domeny aplikacji ...................................................... 400 13.13 Określenie, czy aktualny użytkownik jest członkiem danej grupy Windows ..... . ..... 403 13.14 Ograniczanie użytkowników mających prawo do wykonania kodu ................. 406 13.15 Personifikacja użytkownika systemu Windows ............................... 4 1 1 14 Kryptografia. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415 14.1 Utworzenie kryptograficznie losowego numeru ....................... . ........ 4 16 14.2 Wyliczanie kodu hash dla hasta.. . ......................................... 4 17 14.3 Wyliczanie kodu hash dla pliku ..... . ...................................... 420 14.4 Weryfikacja kodu hash ..................................... . .... . ....... 42 1 14.5 Zapewnienie integralności danych przy użyciu kodu hash z kluczem................. 423
vlli C# - Księga przykładów 14.6 Zabezpieczenie pliku przy użyciu symetrycznego szyfrowania ......... . .. . . . . . . . . . 426 14.7 Wyprowadzenie klucza szyfrowania symetrycznego z hasła..... . ... . . . ......... . . 4 3 1 14.8 Bezpieczne wysyłanie utajnionych danych przy użyciu szyfrowania asymetrycznego . .. . 4 3 3 14.9 Bezpieczne zapamiętywanie klucza szyfrowania asymetrycznego. ... . . . . ........... 4 38 14.10 Bezpieczna wymiana symetrycznego klucza sesji ..... . . . ... . . ...... . . . ....... 4 4 1 1 5 Współdziałanie z niezarządzanym kodem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445 15.1 Wywołanie funkcji z niezarządzanej biblioteki DLL .. . ............... . .. . . . . . . ... 446 15.2 Przejęcie uchwytu kontrolki, okna lub pliku .. . . . . ..... . ............ . ... . .. . .. . 448 15.3 Wywołanie niezarządzanej funkcji wykorzystującej strukturę . . . ... . ..... . ......... 450 15.4 Wywołanie niezarządzanej funkcji posługującej się wywołaniem zwrotnym . ... . ... . . . 45 3 15.5 Odczytywanie niezarządzanej informacji o błędach. . . . ....... . .... . . ........... . 4 5 4 15.6 Użycie komponentu COM w kliencie . NET ................................... . 456 15.7 Szybkie zwolnienie komponentu COM .. . .......... . ....... . . . . . . . ........... 458 15.8 Użycie parametrów opcjonalnych . ... . ..... . .. . . . . .................. . .. . .. . 458 15.9 Użycie kontrolki ActiveX w kliencie . NET .. . .. . ......... ............... . .... . . 460 15.1O Wykorzystanie komponentu .NET przez COM.... . ...................... . ..... 46 1 16 Powszechnie używane Interfejsy I wzorce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463 16.1 Implementacja serializowalnego typu.. . .. . ........ . ...... ...... . ............ 46 4 16.2 Implementacja typu klonowalnego............. . .. . . . .. . ... . ...... . . ........ 468 16.3 Implementacja typów porównywalnych... . ..... . ......................... . . . 471 16.4 Implementacja typu wyliczalnego. . . ..... . .. . ..................... . . . . .. . . . . 475 16.5 Implementacja klasy jednorazowego użycia.. . . .. . . . . . . . ...... . . . ....... . ..... 480 16.6 Implementowanie typu formatowalnego. ..... . .... . ........................ . . 484 16.7 Implementacja niestandardowej klasy wyjątku . ... . . ........................... 487 16.8 Implementacja niestandardowego argumentu zdarzenia.................. . . . . . . . . 491 16.9 Implementacja wzorca pojedynczej instancji ........ . . . . ... . .................. 492 16.1O Implementacja wzorca obserwatora............. . .......................... 493 17 Integracja z systemem Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499 17.1 Dostęp do informacji o środowisku wykonywczym ....... ...................... 500 17.2 Wyszukiwanie wartości zmiennej środowiska . . .... . ........ . ........... . ... . . 503 17.3 Zapisanie zdarzenia do dziennika zdarzeń Windows . . .................... . .... . 5 04 17.4 Dostęp do rejestru Windows . .................................... . . . ... . .. 506 17.5 Utworzenie usługi Windows ... . ............... . . . ..... . .. . ....... . . . .. . .. 5 10 17.6 Utworzenie instalatora usługi Windows .......... . ... . . ...................... 5 14 17.7 Utworzenie skrótu na pulpicie lub w menu Start................................ 5 16 Indeks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
Wstęp Posługiwanie się aplikacjami .NET Framework firmy Microsoft w C# bardziej zależy od możli wości funkcjonalnego wykorzystania biblioteki klas .NET Framework, niż od znajomości jerzyka C#. Podręcznik C#: Księga przykładów wykorzystuje zasoby biblioteki klas .NET Framework i dostarcza określone rozwiązania, dotyczące tak podstawowych, jak i bardziej złożonych prob lemów programistycznych. Każde rozwiązanie (tutaj zwane przepisem) zostało przedstawione w formie opisu, zilustrowanego przykładami odpowiedniego kodu programu. Intencją podręcznika C#: Księga przykładów nie jest nauczanie programowania ani języka C#, ale jeśli czytelnik ma podstawowe doświadczenie w programowaniu aplikacji w środowisku .NET Framework przy użyciu jerzyka C#, niniejsza książka powinna mu posłużyć jako nieoce nione źródło informacji. Można w niej znaleźć gotowe rozwiązania problemów lub odpowiednie do tego wskazówki. C#: Księgaprzykładów może również służyć jako uzupełniające źródło wiedzy o bibliotece klas .NET Framework. Trudno stać się specjalistą od jerzyka C# i biblioteki klas .NET Framework wyłącznie na podstawie lektury: konieczne jest więc eksperymentowanie w praktyce i pisanie coraz co nowych, kolejnych programów. Struktura i zawartość niniejszej książki, jak również osadzenie rozwiązań w czasie rzeczywistym, oferują dobrą pozycję startową dla rozpoczęcia własnych prób. Uwaga Cały zamieszczony w niniejszej książce kod został napisany i przetestowany w .NET Framework wersji 1.1. W wielu przypadkach przykładowe kody działają również w wersji 1.0, ale nie można udzielić gwarancji, gdyż nie zostały przetestowane. Próbki kodu Sekwencje kodu dla wszystkich przepisów w C#: Księga przykładów są dostępne online, pod adresem http:l/microsoft.comlmspmslbooks/6456.asp. Aby pobrać przykładowe pliki, należy klik nąć łącze Companion Content w menu More Information, znajdującym się w prawej części ekranu. Nastąpi załadowanie strony Companion Content, zawierającej odsyłacz umożliwiający ściągnięcie tych plików. Aby zainstalować przykładowe pliki, należy kliknąć odsyłacz Download The Book's Sample Filcs i wykonać instrukcje w programie ustawień. Odsyłacz do przykłado wego kodu zostanie dołączony do menu Start. Kod jest zapisany jako zestaw rozwiązań i projektów Visual Studio .NET 2003, podzie lony na rozdziały i numery przepisów. Każdy rozdział stanowi oddzielne rozwiązanie, a prze pis - oddzielny projekt, w obrębie rozwiązania dla danego rozdziału. Niektóre z przepisów w rozdziałach 11 i 12, demonstrujących programowanie sieciowe, zawierają oddzielne projekty składowe dla klienta i serwera.
x C# - Księga przykładów Chociaż wszystkie przykłady są napisane jako projekty Visual Studio .NET, większość z nich zawiera pojedynczy plik źródłowy, który można skompilować i uruchomić niezależnie od Visual Studio .NET. Jeśli system Visual Studio .NET 2003 nie jest używany, można zlokalizować kod dla danego przepisu, dzięki poruszaniu się po strukturze katalogowej kodu z przykładu; np. aby znaleźć kod przepisu 4.3, należy przejrzeć katalog „Chapter04\Recipe04-03". Przy używaniu kompilatora wywoływanego z linii poleceń, należy pamiętać o włączeniu odniesień do wszyst kich wymaganych asemblacji biblioteki klas .NET. Niektóre przykładowe aplikacje wymagają dołączenia argumentów z linii poleceń. Zostaną one opisane w dalszej części przepisu. Przy użyciu Visual Studio .NET można wprowadzić te argumenty do właściwości projektu (pod węzłem debugowania - debugging - pozycji Con figuration Properties). Aby wejść w katalog lub wykorzystać nazwy plików zawierające spacje, należy całą nazwę ująć w cudzysłów. Do zainstalowania dwóch wirtualnych katalogów, użytych w przykładach dla rozdziałów 7 oraz 1 2, potrzebne są pewne dodatkowe kroki. Zostały one opisane w pliku tekstowym readrne.txt na stronie internetowej umożliwiającej ściągnięcie kodów źródłowych. Wymagania systemu Aby uruchomić kod przykładów, należy dysponować następujacyrn oprogramowaniem: • Microsoft .NET Framework SOK, wersji 1.1 • Microsoft Visual Studio .NET 2003 (zalecany) • Microsoft Windows 2000, Windows XP lub Microsoft Windows Server 2003 • Microsoft SQL Server 2000 lub MSDE - dla przepisów w rozdziale 1O • Microsoft Internet Information Services (IIS) - dla niektórych przepisów w rozdziałach 7 i 12 Minimalna konfiguracja sprzętu: procesor klasy Pentium II 450 MHz, co najmniej 128 MB pamięci RAM przy pracy w systemie Microsoft Windows 2000 lub 256 MB przy pracy w syste mie Windows XP, Windows 2000 Server albo Windows Server 2003, przestrzeń dyskowa około 5 GB w celu zainstalowania Visual Studio .NET 2003. Wymienione parametry stanowią mini mum, jednak zwiększenie dostępnej pojemności dysku znacznie ułatwi projektowanie. Uwaga Wprawdzie treścią książki C#: Księga przykładów jest implementacja platformy .NET Framework dla systemu Windows, równie ważne jest dostarczenie wszystkim pro gramistom C# niezbędnych zasobów informacji - niezależnie od wykorzystywanej przez nich platformy projektu i narzędzi. Większość umieszczonych tutaj przykładów działa we wszystkich implementacjach .NET; wyjątek stanowią te tematy, które nie zostały wdro żone na wszystkich platformach .NET, takich jak Windows Forms, ADO.NET i ASP.NET.
1 Tworzenie aplikacji Ten rozdział przybliża podstawowe czynności, niezbędne przy tworzeniu własnych programów napisanych w języku C#. Znajdują się tu przepisy dotyczące takich czynności, jak: • Budowa aplikacji trybu tekstowego oraz aplikacji postaci formularza Windows (przepisy 1.1 i 1.2). • Tworzenie i użytkowanie modułów programowych i bibliotek (przepisy 1.3 i 1.4). • Dostęp z aplikacji do argumentów linii poleceń (przepis 1.5). • Wykorzystanie dyrektyw i atrybutów kompilatora do selektywnego włączania kodu podczas budowy (przepis 1.6). • Dostęp do elementów programu, ucworwnych w innych językach, których nazwy kolidują ze słowami kluczowymi języka C# (przepis 1.7). • Nadanie asembłacjom silnych nazw i weryfikacja asemblacji o silnej nazwie (przepisy 1.8, 1.9, 1.10, i 1.11). • Podpisywanie asembłacji podpisem cyfrowym Microsoft Authenticode (przepisy 1.12 i 1.13). • Zarządzanie asemblacjami o dzielonym dostępie, przechowywanymi w globalny buforze asemblacji (GAC- Global Assembly Cache) (przepis 1.14). • Ochrona przed osobami mogącymi zdekompilować asembłację (przepis 1. 15). Uwaga Wszystkie narzędzia dyskutowane w niniejszym rozdziale są dostarczane łącznie z platformą Microsoft .NET Framework lub .NET Framework SDK. Narzędzia stanowiące część Framework znajdują się w głównym katalogu wersji Framework prze znaczonej dla czytelników niniejszej książki. Na przykład: po zainstalowaniu wersji .NET Framework 1 .1 w lokalizacji domyślnej, znajdą się w katalogu C:\WINDOWS\Microsoft. NEnFramework\v1 .1 .4322. Proces instalacyjny .NET doda automatycznie ten katalog do ścieżki środowiska użytkownika. Ciqgdalszy na nastrpnejstronie
2 C# - Księga przykładów Cil/gdalszyzpoprzedniejstrony Narzędzia dostarczone z platformą SOK znajdują się w podkatalogu Bin - wewnątrz katalogu, w którym zostanie później zainstalowana platforma SOK. Ten katalog nie jest automatycznie dodawany do ścieżki dostępu; należy ręcznie edytować ścieżkę, aby mieć wygodny dostęp do narzędzi. Większość narzędzi umożliwia tworzenie zarówno długich, jak i skróconych formatów dla przełączników w linii poleceń, kontrolujących ich funkcjonalność. Niniejszy rozdział zawsze podaje długi format, który wymaga wprawdzie więcej pisania, ale niesie również więcej informacji. Skrócony format każdego przełącznika można znaleźć w dokumentacji narzędziowej w .NET Framework SOK. 1 .1 Tworzenie aplikacji trybu tekstowego Problem Chcesz zbudować aplikację, która umożliwia użytkownikowi wprowadzanie danych, ale nie wymaga graficznego interfejsu użytkownika Windows (GUI), a na tekstowej konsoli prezentuje wyniki działania i wyświetla znak zachęty. Rozwiązanie Użyj w swojej implementacji metody static o nazwie Main z jednym z poniższych wpisów w co najmniej jednym z twoich plików kodów źródłowych. public static void Main ( ) ; public static void Main ( st ring [ ] args ) ; public static int Main ( ) ; public static int Main ( st ring ( ] a rgs ) ; Podczas kompilacji należy użyć przełącznika ltarget:exekompilatora C# (csc.exe). Omówienie Domyślnie kompilator C# tworzy aplikację trybu tekstowego (Consokapplication), nie jest więc konieczny przełącznik ltarget:exe. Użycie go może być przydame, jeśli skrypty służące do budowy aplikacji mają być wykorzystywane wielokrotnie. Podane niżej przykłady przedstawiają klasę o nazwie ConsokUtils (zasoby konsoli), zdefiniowaną w pliku o nazwie ConsoleUtils.cs: using System ; public class ConsoleUtil s { li Metoda wyświetlenia znaku zachęty i odczytu odpowiedzi z konsoli public static st ring ReadSt ring ( st ring msg ) { Console.Write (msg ) ;
} retu rn System . Console. Readline() ; } Rozdział 1 : Tworzenie aplikacji 3 li Metoda wyświetlenia komunikatu w konsoli public static void WriteSt ring(st ring msg ) { System . Console . Writeline(msg ) ; } li Główna metoda używana do testowania metod ConsoleUtility public static void Main() { } st ring name = ReadSt ring(" Please enter you r name : " ) ; li Powitanie czytelnika WriteSt ring("Welcome to the C# P rog ramme r ' s Cookbook, " + name ) ; I Aby wbudować klasę ConsokUtils do aplikacji konsolowej o nazwie ConsoleUtils.exe, należy użyć polecenia csc /target:exe ConsoleUtils.cs. Wynikową asemblację wykonywalną można uruchomić bezpośrednio z linii poleceń. Metoda Main aplikacji ConsoleUtiłs.exe poprosi użyt kownika o wprowadzenie imienia, a następnie powita, jak pokazano niżej. Please enter you r name : Rupert Welcome to the C# Prog rammer's Cookbook , Rupe rt W rzeczywistości aplikacje rzadko składają się z pojedynczych plików źródłowych. Na przykład przytoczona tutaj klasa HelloWorld wykorzystuje klasę ConsokUtils do wyświetlenia polecenia „Hdlo, world" na konsoli. (HelloWorldjest przechowywane w pliku HelloWorld.es). public class HelloWo rld { public static void Main() { ConsoleUtils . WriteSt ring("Hello , wo rld" ) ; } } Aby utworzyć aplikację konsolową złożoną z co najmniej dwóch plików kodu źródłowego, należy określić wszystkie pliki źródłowe jako argumenty kompilatora. Na przykład przytoczone niżej polecenie utworzy aplikację o nazwie MyFirstApp.exe z plików źródłowych HelloWorld.es i ConsoleUtils.cs. csc /ta rget : exe /main : HelloWo rld /ou t : MyFirstApp. exe HelloWo rld . cs ConsoleUtils . cs Przełącznik /out pozwala określić nazwę skompilowanej asemblacji. Jeśli nie wstanie użyty, będzie nadana nazwa pliku zapisanego jako pierwszy - w danym przypadku HelloWorld.es. Ponieważ obie klasy: HelloWorld i ConsokUtils zawierają metody Main, kompilator nie może automatycznie określić, która metoda reprezentuje właściwy punkt wejściowy (entty point) asemblacji. Należy użyć przełącznika kompilatora /main, aby zidentyfikować nazwę klasy zawie rającej właściwy dla aplikacji punkt wejściowy (entrypoint).
4 C# - Księga przykładów 1 .2 Utworzenie aplikacji Windows Problem Chcesz utworzyć aplikację wykorzystującą GUI (Graphical User Interface - graficzny interfejs użytkownika) w oparciu o formularze Windows. Rozwiązanie Zastosuj metodę statyczną o nazwie Main w swoim pliku kodu źródłowego. Utwórz w metodzie Main instancję klasy, rozszerzającej klasę System. Windows.Forms.Form Qest to główny formu larz twojej aplikacji). Przekaż ten formularz jako obiekt do metody static o nazwie Run klasy System. Windows.Forms.Application. Wykorzystaj przełącznik /target:winexe dla kompilatora C# (csc.exe) przy kompilacji swojej asemblacji. Omówienie Między budową aplikacji z prostym interfejsem GUI dla systemu Windows a utworzeniem w pełni ukształtowanej aplikacji w oparciu o cen system istnieje ogromna różnica. Są jednak czynności, które trzeba wykonać bez względu na to, czy się pisze odpowiednik „Hello World" systemu Windows, czy opracowanie następnej wersji systemu Microsoft Word. Chodzi tu mię dzy innymi o następujące czynności: • Dla każdego formularza wymaganego w aplikacji należy utworzyć klasę, która obejmuje również klasę System. Windows.Forms.Form. • W każdej z klas formularzy należy zadeklarować pola reprezentujące kontrolki, które w danym formularzu wystąpią, np. przyciski, etykiety, listy i ramki tekstowe. Te pola powinny być zadeklarowane jako prywame (private} łub co najmniej chronione (protected}, aby inne elementy programu nie miały do nich bezpośredniego dostępu. Dla ujawnienia metod lub właściwości kontrolek należy zaimplementować niezbędne pola w klasie formu larza, umożliwiając niebezpośredni, kontrolowany dostęp. • W klasie formularza należy zadeklarować metody zarządzające zdarzeniami wywoływa nymi przez kontrolki, takimi jak kliknięcie przycisku myszy lub wciśnięcie klawisza, kiedy aktywnym elementem sterującym jest ramka tekstowa. Te metody powinny być prywatne (private) lub chronione (protected) i stosować się do standardu .NET eventpattern (wzór zdarzenia), opisanego w przepisie I6.10. Dotyczy to metod, w których wstanie zdefinio wana funkcjonalność aplikacji (lub metod przez nie wywoływanych). • Należy zadeklarować konstruktora dla klasy formularza, który nada instancje wszystkim kontrolkom formularza i skonfiguruje ich stan początkowy (rozmiar, kolor, pozycję, zawar tość itd.). Konstruktor powinien także przypisać odpowiednie metody programu obsługi zdarzeń danej klasy do zdarzeń poszczególnych kontrolek. • Należy zadeklarować metodę statyczną (static) o nazwie Main - zazwyczaj w postaci pola klasy głównego formularza danej aplikacji. Ta metoda stanowi punkt wejściowy (entrypoint) aplikacji i jako taka może posiadać re same podpisy, co metody wymienione w przepisie I. I.
Rozdział 1 : Tworzenie aplikacji 5 W metodzie Main należy utworzyć instancję głównego formularza aplikacji i przekazać ją jako argument do metody static Application.Run. To spowoduje wizualizację głównego formularza i uruchomi dla bieżącego wątku standardową pętlę poleceń Windows, w wyniku czego możliwe będzie przeniesienie danych wejściowych użytkownika (typu naciśnięcia kla wisza, kliknięcia myszą itp.) do utworzonej aplikacji w charakterze zdarzeń. Klasa WelcomeForm, przedstawiona w poniższym fragmencie programu, stanowi prostą aplikację formularzy Windows (Windows Forms), demonstrując wymienione wyżej techniki. W trakcie działania program monituje użytkownika, żeby wprowadził imię, a następnie wyświetla okno poleceń z powitaniem w programie C#: Ksirgaprzykładów. using System . Windows . Fo rms ; public class WelcomeForm : Form { li Prywatni członkowie p rzechowuj ący odnośniki do kontrolek formula rza . p rivate Label label l ; p rivate TextBox textBox l ; p rivate Button buttonl ; l i Konst ruktor służący d o tworzenia instancj i fo rmula rza i konfigu rowania li kont rolek. public WelcomeFo rm ( ) { li Utworzenie instancj i kont rolek użytych w formula rzu . this . labell = new Label ( ) ; this . textBoxl = new TextBox ( ) ; this . buttonl = new Button ( ) ; li Zawieszenie logiki układu formula rza na czas konfigu rowania li kont rolek . this . SuspendLayout ( ) ; li Konfigu rowanie label l, wyświetlaj ącej zachętę dla użytkownika . this . labell . Location = new System . D rawing . Point ( 16 , 36 ) ; this . label l . Name = " labell" ; this . labell . Size = new System . D rawing . Size ( 128 , 16) ; this . labell . Tab!ndex = 0 ; this . labell . Text = " Please ente r you r name : " ; li Konfigu rowanie textBoxl, odbie raj ącego dane użytkownika . this . textBoxl . Location = new System . Drawing . Point ( 152 , 32 ) ; this . textBoxl . Name = "textBoxl" ; this . textBoxl . Tabindex = 1 ; this . textBoxl . Text = " " ; li konfigu rowanie buttonl , klikanego p rzez użytkownika . this . buttonl . Location = new System . Drawing . Point ( 109 , 89) ; this . buttonl . Name = " buttonl" ; this . buttonl . Tabindex = 2 ; this . buttonl . Text = "Ente r" ; this . buttonl . Click += new System . EventHandle r ( this . buttonl_Click) ;
6 C# - Księga przykładów } } li Konfigu rowanie formula rza WelcomeForm i dodanie kont rolek . this . ClientSize = new System . D rawing . Size ( 292 , 126 ) ; this . Cont rols . Add ( this . buttonl ) ; this . Cont rols . Add ( this . textBoxl ) ; this . Cont rols . Add ( this . label l ) ; this . Name = "forml " ; this . Text = "C# Prog ramme r ' s Cookbook" ; li Przywrócenie logiki układu dla formula rza this . ResumeLayout ( false ) ; li Punkt wej ścia aplikacj i , two rzy instancję formularza i u ruchamia li standa rdową pętlę w bieżącym wątku . public static void Main ( ) { Application . Run( new WelcomeForm( ) ) ; } li Obsługa zda rzeń wywołana po kliknięciu p rzycisku Ente r . private void buttonl_Click (obj ect sende r, System . EventArgs e) { li Wypisanie komunikatu debugowania na konsoli System . Console . WriteLine ( " User ente red : " + textBoxl . Text ) ; li Wyświetlenie powitania j ako okna komunikatu MessageBox . Show("Welcome to the C# Prog ramme r's Cookbook , " + textBoxl . Text , "C# Prog rammer ' s Cookbook" ) ; } Aby wbudować klasę WelcomeForm (przechowywaną w pliku o nazwie WelcomeForm.cs) w aplikację, należy wykorzystać polecenie csc /target:winexe WełcomeFonn.cs. Przełącznik ltarget:winexe poinformuje kompilator, że aplikacja jest uruchamiana w oparciu o system Windows. W rezultacie kompilator utworzy moduł wykonywalny w taki sposób, żeby w ogóle nie angażować konsoli podczas działania aplikacji. Jeśli do budowy aplikacji typu formularz Windows zostanie użyty przełącznik ltarget:exe zamiast ltarget:winexe, będzie ona również dzia łała prawidłowo, ale dodatkowo będzie widoczne okno konsoli. Przy produkcji oprogramowa nia finalnego jest to niepożądane, jednak okno konsoli jest pomocne przy pisaniu procedur usuwania błędów, rejestrowania podczas rozbudowy i testowania aplikacji Windows Forms. Można również pisać na konsoli, używając metod Write i WriteLine klasy System.Consol.e. Rysunek 1-1 przedstawia aplikację WelcomeForm.exe, wykonującą pozdrowienie użytkow nika o imieniu Rupert. Ta wersja aplikacji została utworzona przy użyciu przełącznika kompi latora ltarget:exe, dzięki czemu powstało widoczne okno konsoli, w którym można obserwować dane wyjściowe instrukcji Consol.e. WriteLine programu obsługi zdarzeń button]_Click.
Rozdział 1 : Tworzenie aplikacji 7 Rysunek 1-1 Prosta aplikacja Formularzy Windows (Windows Forms) . Uwaga Budowa dużych aplikacji w oparciu o GUI jest zajęciem czasochłonnym, które wymaga prawidłowegonadawania instancji, jak również konfigurowania i wiązania ze sobą wielu formularzy i kontrolek. Visual Studio .NET firmy Microsoft umożliwia automatyzację wielu prac związanych z budowaniem aplikacji graficznych . Tworzenie dużych aplikacji graficznych bez pomocy takich narzędzi, jak Visual Studio .NET, zajmuje o wiele więcej czasu, jest trudniejsze i zwiększa prawdopodobieństwo pojawienia się błędów w kodzie wynikowym . 1 .3 Utworzenie i wykorzystanie modułu kodu Problem Chciałbyś uzyskać jedną lub więcej z poniższych opcji: • Poprawić sprawność aplikacji i wykorzystanie przez nią pamięci poprzez ładowanie przez moduł runtime rzadko używanych typów tylko wtedy, gdy zachodzi rzeczywista potrzeba ich obecności. • Skompilować typy zapisane w języku C# do formularza, umożliwiającego wbudowanie go do aplikacji napisanych w innych językach .NET. • Wykorzystać typy zaprojektowane w innych językach do potrzeb aplikacji w języku C#. Rozwiązanie Zbuduj swój kod źródłowy w języku C# w postaci modułu przy użyciu przełącznika kom płiatora ltarget:module. Aby włączyć istniejące moduły do swego programu, użyj przełącznika kompilatora laddmodule.
8 C# - Księga przykładów Omówienie Moduły stanowią elementy budowy asemblacji .NET. Każdy z nich składa się z pojedynczego pliku, który zawiera wyszczególnione niżej elementy: • Kod Microsoft lntermediate Language (MSIL), utworzony z kodu źródłowego w języku C# podczas kompilacji. • Metadane, opisujące typy zawarte w module. • Zasoby, takie jak ikony i tablice łańcuchów, używane przez cypy w module. Asemblacje składają się z jednego lub więcej modułów oraz z manifestu asemblacji. W przy padku pojedynczego modułu, moduł i manifest asemblacji są zazwyczaj dla wygody umiesz czone w pojedynczym pliku. Gdy modułów jest więcej, asemblacja stanowi logiczne połączenie więcej nii jednego pliku, które trzeba rozpowszechaniać jako kompletną jednostkę. W tej sytuacji manifest asemblacji zostanie umieszczony w osobnym pliku albo wbudowany w jeden z modułów. Tworzenie asemblacji z wielu modułów sprawia, ie zarządzanie staje się bardziej skompli kowane, a jej rozpowszechnianie trudniejsze, jednak w pewnych warunkach moduły oferują znaczne korzyści, takie jak: • Moduł zostanie załadowany przez moduł runtime cylko wtedy, gdy potrzebne są typy zde finiowane w cym module. Jeśli więc w grę wchodzi zestaw typów rzadko uiywanych przez aplikację, moina umieścić je w osobnym module, który zostanie załadowany cylko w razie konieczności. To rozwiązanie daje następujące korzyści: O Zwiększa funkcjonalność aplikacji, szczególnie przy pracy w sieci. O Pozwala na minimalizację wykorzystania pamięci. • Moiliwość wykorzystania wielu róinych języków przy pisaniu aplikacji, króra działa pod kontrolą wspólnego modułu runtime CLR (Common Language Runcime), jest wielką zaletą .NET Framework. Jednakże kompilator C# nie może skompilować kodu w Microsoft Visual Basic .NET lub COBOL .NET w celu jego dołączenia do asemblacji. Najpierw trzeba zastosować kompilator określonego języka, żeby przekształcić kod źródłowy w strukturę MSIL, którą kompilator C# może włączyć do całości, czyli w moduł. Jeśli inni programiści mają korzystać z tych typów, które zostały utworzone w języku C#, naleiy równiei włączyć je do modułu. Aby skompilować plik źródłowy o nazwie ConsoleUtils.cs do postaci modułu, naleiy uiyć pole cenia csc /target:module ConsoleUtils.cs, w wyniku czego zostanie utworzony plik o nazwie ConsoleUtils.netmodule. Rozszerzenie netmodule jest domyślne dla wszystkich modułów, a nazwa pliku taka sama, jak nazwa pliku źródłowego w języku C#. Moina równiei tworzyć modułyzwielu plikówźródłowych, cospowoduje powstanie pojedyn czegopliku (modułu), zawierającego MSILi metadane dlawszystkich typów,zawartychwewszyst kich plikach źródłowych. Polecenie csc /target:module ConsoleUtils.cs WindowsUtils.cs kompiluje dwa pliki źródłowe o nazwach ConsoleUtils.cs i WindowsUtiłs.cs, aby utworzyć moduł o nazwie ConsoleUtiłs.netmodule. Moduł ten nosi nazwę pochodną od nazwy pierw szego wymienionego pliku tekstowego, o ile nie zostanie ona zastąpiona przy użyciu przełącz nika kompilatora /out. Na przykład polecenie csc /target:moclule /out:Utilities.netmodule ConsolcUtils.cs WindowsUtils.cs utworzy moduł o nazwie Utilicies.netmodule.
Rozdział 1 : Tworzenie aplikacji 9 Aby utworzyć asemblację składającą się z wielu modułów, należy posłużyć się przełącznikiem kompilatora laddmodule. Aby utworzyć plik wykonywalny o nazwie MyFirstApp.exe z dwóch modułów o nazwach WindowsUtils.netmodule i ConsoleUtils.netmodule oraz z dwóch plików źródłowych o nazwach SourceOne.cs i SourceTwo.cs, należy użyć polecenia csc /out:MyFust App.exe/target:exe/addmodule:WmdowsUtils.netmodule,ConsoleUtils.netmoduleSoW'CC One.es SourceTwo.cs. To polecenie utworzy asemblację składającą się z następujących plików: • MyFirstApp.exe, który zawiera manifest asemblacji oraz MSIL dla typów zadeklarowanych w plikach źródłowych SourceOne.cs i SourceTwo.cs. • ConsoleUtils.netmodułe i WindowsUtils.netmodułe, które teraz są integralnymi składni kami złożonej z wielu plików asemblacji, ale nie zostały zmienione w tym procesie kompila cji (przy próbie uruchomienia MyFirstApp.exe bez obecności modułów netmodules, system zgłosi wyjątek System.IO.FileNotFoundException). 1 .4 Utworzenie biblioteki kodów i korzystanie z niej Problem Chcesz umieścić zestaw funkcjonalności w bibliotece kodów wielokrotnego użycia, dzięki czemu różne aplikacje mogą po wywołaniu z niego korzystać. Rozwiązanie Aby utworzyć bibliotekę, użyj przełącznika ltarget:library dla kompilatora C# (csc.exe) pod czas kompilowania asemblacji. Aby się odnieść do biblioteki, użyj przełącznika kompilatora C# lreference podczas kompilowania aplikacji i określ nazwy żądanych bibliotek. Omówienie W przepisie 1.1 pokazano, jak utworzyć aplikację o nazwie FirstApp.exe z dwóch plików źródłowych: ConsoleUtils.cs i HełloWorld.cs. Plik ConsoleUtils.cs zawiera klasę ConsoleUtils, która zapewnia metody upraszczające interakcję z konsolą Windows. Gdyby rozszerzyć zakres funkcjonalności klasy ConsoleUtils, mogłaby ona zawierać funkcjonalność użyteczną dla wielu aplikacji. Tak więc, zamiast włączać kod źródłowy dla ConsoleUtils do każdej aplikacji, można go umieścić w bibliotece i wykorzystywać niezależnie. Aby umieścić w bibliotece plik ConsoleUtils.cs, należy zastosować polecenie csc /target: library ConsoleUtils.cs. Spowoduje ono utworzenie biblioteki o nazwie ConsoleUtils.dłł. Aby utworzyć bibliotekę z wielu plików źródłowych, należy zapisać nazwę każdego pliku na końcu polecenia. Używając przełącznika kompilatora !out, można także określić nazwę biblioteki; w innym wypadku biblioteka będzie nosiła nazwę pierwszego pliku źródłowego. Na przykład, by utworzyć bibliotekę o nazwie MyFirstLibrary.dll dwóch plików źródłowych o nazwach ConsoleUtils.cs i WindowsUtils.cs, należy użyć polecenia csc /out:MyFirstLlbrary.dll /target: library ConsoleUtils.csWmdowsUtils.cs.
10 C# - Księga przykładów Przed rozpowszechnieniem biblioteki można zastosować silne nazwy, aby nikt oprócz autora nie mógł modyfikować asemblacji i przesyłać jej jako własnego oryginału. Silne nazewnictwo biblioteki pozwala także na instalowanie jej w globalnym buforze asemblacji (GAC - Global Assembly Cache), co ułatwia wielokrotne wykorzystywanie (w przepisie 1 .9 podano sposób zastosowania silnego nazewnictwa w asemblacji, a w przepisie 1 .14 - metodę instalacji asembla cji o silnej nazwie w GAC). Można również podpisać bibliotekę podpisem elektronicznym Aur henticode; będzie to udokumentowaniem faktu, kro jest wydawcą danej asemblacji - metody podpisywania asemblacji przy pomocy Authenticode zawiera przepis 1 .1 2. Wceluskompilowania asemblacji zależnej od typów zadeklarowanych w bibliotekach zewnętrz nych, trzeba poinformować kompilator, o które biblioteki chodzi - używając przdącznika kom pilatora !reference. Na przykład, aby skompilować plik źródłowy HelloWorld.es z przepisu 1 .1 , gdy klasa ConsoleUtils znajduje się w bibliotece ConsoleUtils.dll, należy zastosować polecenie csc /referenc.e:ConsoleUtils.dll HelloWorld.es. Nasuwają się trzy uwagi godne zapamiętania: • Odwołując się do więcej niż jednej biblioteki, należy rozdzielić ich nazwy przecinkiem łub średnikiem, ale nie spacją. Na przykład: /referenc.e:ConsoleUtils.dll,WindowsUtils.dll. • Jeżeli kody źródłowe bibliotek nie znajdują się w rym samym katalogu, należy użyć prze łącznika kompilatora !lib dla określenia dodatkowych katalogów, które kompilator ma przeszukać w celu odnalezienia bibliotek. Na przykład: /lib:c:\CommonLibraries, c:\Dev\ ThirdPartyLibs. • Jeżeli odniesienie dotyczy biblioteki, która stanowi asemblację wielu plików, należy sięgnąć do tego pliku, który zawiera manifest asemblacji. Informacje na temat asemblacji wielu plików podaje przepis 1 .3. 1 .5 Dostęp do argumentów linii poleceń Problem Chcesz mieć dostęp do argumentów określonych w linii poleceń w trakcie wykonywania aplikacji. Rozwiązanie Użyj podpisu do metody Main, co pozwoli ułożyć argumenty w linii poleceń w formie tablicy łańcuchów. Alternatywnie możesz sięgnąć do argumentów w linii poleceń z dowolnego miejsca kodu przy użyciu pól typu staticklasy System.Environments. Omówienie Zadeklarowanie metody Main aplikacji z jednym z poniższych podpisów umożliwia dostęp do argumentów linii poleceń w postaci tablicy łańcuchów. public static void Main ( st ring ( J a rg s ) {} public static int Main ( st ring [ ) a rg s ) {}
Rozdział 1 : Tworzenie aplikacji 1 1 Podczas wykonywania programu, argument args b�zie zawierał łańcuch (string) dla każdej war tości wprowadzonej do linii poleceń po nazwie aplikacji. Metoda Main w poniższym przykładzie bada każdy przekazany argument linii poleceń i wyświetla je na konsoli. public class CmdLineArgExample { public static void Main ( st ring [ ] a rgs ) { li Kolej ne wykonywanie a rgumentów wie rsza polecenia foreach ( st ring s in a rgs ) { System . Console . WriteLine ( s ) ; } } } Ważne jest zrozwnienie sposobu przekazywania argumentów do aplikacji. Jeśli wykonywany jest przykład CmdlineArgExamp/.e przy użyciu następującego polecenia: CmdLineArgExample "one \ " two\" th ree" four ' five to aplikacja wygeneruje na konsoli następujące dane wyjściowe: one " two" fou r ' five six ' three six ' Należy zauważyć, że inaczej niż w języku C lub C++, nazwa aplikacji nie jest umieszczona w tablicy argumentów. Użycie znaku podwójnego cudzysłowu (") sprawia, że jako pojedynczy argument traktowane jest więcej niż jedno słowo, podczas gdy cudzysłów pojedynczy (') tego nie powoduje. Można włączyć podwójny cudzysłów do argumentu poprzez ujęcie go lewym ukośnikiem (\). Wszystkie spacje zostaną z linii poleceń usunięte, o ile nie są ujęte w podwójny cudzysłów. Jeśli konieczny jest dostęp do argumentów linii poleceń - w innych miejscach kodu, niż metoda Main- należy przetworzyć argumentylinii poleceńw swojej metodzie Main i zapamiętać je do póź niejszego wykorzystania. Alternatywnie można użyć klasy System.Environment, która zawiera dwa pola static dostarczające informację o linii poleceń: Commandline i GetCommandlineArgs. Właś ciwość Commandline zwraca łańcuch zawierający całą linię poleceń, która uruchomiła aktualny proces. W zależności od systemu operacyjnego, w którym działa dana aplikacja, nazwa aplikacji może być poprzedzona informacją o ścieżce. Systemy Microsoft Windows NT 4.0, Windows 2000 i Windows XP nie dołączają informacji o ścieżce; systemy Windows 98 i Windows ME infor mację tę dołączają. Metoda GetCommandLineArgs przekazuje zwrotnie tablicę zawierającą argu menty linii poleceń. Tablica ta może być przetworzona w taki sam sposób, jak tablica przekazana do metody Main, omówiona na początku tego przepisu. W przeciwieństwie do tablicy przeka zywanej do metody Main pierwszym dementem w tablicy, przekazanej zwrotnie przez metodę GetCommandLineArgs, jest nazwa aplikacji.
12 C# - Księga przykładów 1 .6 Selektywne włączanie kodu podczas jego budowy Problem Chcesz selektywnie włączać i wyłączać fragmenty kodu źródłowego ze skompilowanej asemblacji. Rozwiązanie Użyj dyrektyw preprocesora #if, #elif, #else i #endifw celu identyfikacji bloków kodu, które mają być warunkowo dołączane do asemblacji. Wykorzystaj atrybut System.Diagnostics.Conditional Attributecelem zdefiniowania metod, które mająbyćwywoływane wyłączniewarunkowo. Możesz kontrolować dołączanie warunkowego kodu dzięki zastosowaniu w kodzie dyrektyw #dejine i #umie/ lub poprn:z użycie przełącznika !de.fine przy uruchomieniu kompilatora C#. Omówienie Jeśli aplikacja ma działać w sposób zależny od takich czynników, jak platforma czy środowisko, w którym się znajduje, należy wbudować w logikę kodu mechanizmy sprawdzające modułu runtime, które będą przełączać wykonywanie poszczególnych wariantów. Jednakie takie roz wiązanie może zaowocować zwięk."7.oną objętością kodu i obniżyć efektywność wykonania, zwłaszcza gdy chodzi o wicie wariantów lub wicie lokalizacji. Alternatywnym rozwiązaniem jest utworzenie różnych wersji danej aplikacji, działających na różnych docelowych platformach i w różnych środowiskach. Taka realizacja nie wyklucza ani zwiększenia objętości kodu, ani pogorszenia efektywności, może się okazać jednak optymalnym rozwiązaniem, jeśli dla każdej wersji musi zadziałać inny kod źródłowy. Tak więc język C# umożliwia dostosowanie do potrzeb klienta wersji aplikacji od podstaw, od pojedynczego kodu. Dyrektywy preprocesora #if, #elif, #else i #endifumożliwiają identyfikację tych bloków kodu, które kompilator może włączyć do asemblacji tylko wtedy, gdy podczas kompilacji wstaną zdefiniowane specjalne symbole. Te symbole funkcjonują jako przełączniki „on/off' i nie posia dają wartości: symbol jest po prostu zdefiniowany lub nie. Aby zdefiniować symbol, można użyć albo dyrektywy #de.fine, albo wykorzystać przełącznik kompilatora /de.fine. Symbole zde finiowane przy pomocy #de.fine pozostają aktywne do końca pliku, w którym je zdefiniowano. Symbole zdefiniowane przy pomocy przełącznika kompilatora !de.fine są aktywne we wszystkich plikach źródłowych, które mają być kompilowane. Aby symbol utworzony przy użyciu prze łącznika kompilatora /de.fine uczynić z powrotem niezdefiniowanym, przewidziana jest w języku C# dyrektywa #undef, zapewniająca brak zdefiniowania symbolu w określonych plikach źródło wych. Wszystkie dyrektywy #de.fine i #undefmuszą być umieszczone przed kodem, na początku pliku źródłowego, a także przed wszystkimi dyrektywami using. Dla symboli rozróżniana jest wielkość liter. W poniższym przykładzie kod przypisuje różne wartości lokalnej zmiennej platformName, zależnie od tego, czy symbole winXP, win2000, winNTlub win98 są zdefiniowane. Nagłówek kodu definiuje symbole win2000 i release (w tym przykładzie nie wykorzystany) i anuluje defi nicję symbolu win98 w przypadku, gdy był on zdefiniowany w linii poleceń kompilatora.
Rozdział 1 : Tworzenie aplikacji 13 #define win2000 #define release #undef win98 using System ; public class ConditionalExample { public static void Main ( ) { } } li Deklaracj a łańcucha st ring platformName ; #if winXP li Kompilacj a dla Windows XP platformName = "Mic rosoft Windows XP" ; #elif win2000 li Kompilacj a dla Windows 2000 platformName = "Mic rosoft Windows 2000 " ; #elif winNT li Kompilacj a dla Windows NT platformName = "Mic rosoft Windows NT" ; #elif win98 li Kompilacj a dla Windows 98 platformName = "Microsoft Windows 98" ; #else li Nieznana platforma platfo rmName = " Unknown " ; #endif Console . WriteLine ( platformName ) ; Aby utworzyć klasę ConditionalExample (zawartą w pliku o nazwie ConditionalExample.cs) i zdefiniować symbole winXP i DEBUG (nie wykorzystany w niniejszym przykładzie), należy użyć polecenia csc /define:winXP;DEBUG ConditionalExample.cs. Konstrukcja #if.. #endif pozwoli oszacować zdania #ifi #eliftylko wtedy, gdy stwierdzi, że jedna z wartości jest prawdziwa. Oznacza to, że jeśli wstanie zdefiniowanych wiele symboli (na przykład winXP i win2000), ważna jest kolejność ucworwnych zdań. Kompilator dołączy tylko kod ze zdania, które oceni jako prawdziwe. Jeśli żadne zdanie nie jest prawdziwe, kompi lator umieści kod w zdaniu #else. Można również użyć operatorów logicznych, aby oprzeć kompilację warunkową na więcej niż jednym symbolu. Tabela 1 - 1 podsumowuje operatory posiłkujące się dyrektywą #if..#endif. Tabela 1-1 Operatory logiczne posiłkujące się dyrektywą #if.. #endif Operator Przykład Opis #ifwinXP == true Równość. Przypisuje wartość true, jeśli symbol winXP jest zdefiniowany. Równoważny z #ifwinXP. != #ifwin.XP != true Nierówność. Przypisuje wartość true, jeśli symbol winXP nie jest zdefiniowany. Równoważny z #if!winXP. && #ifwinXP && release Logiczne AND. Przypisuje wartość true, jeśli obydwa symbole winXP i release są zdefiniowane. Ciq.gdalszy na nastrpnejstronie
14 C# - Księga przykładów Tabela 1-1 Operatory logiczne posiłkujące się dyrektywą #if..#endif Operator Przykład Opis Ciqgda/szy zpoprzedniejstrony li #ifwinXP li release Logiczne OR. Przypisuje wartość true, jeśli któryś z symboli winXP lub release jest zdefiniowany. o #if(winXP li win2000) && release Nawiasy umożliwiają grupowanie wyrażeń. Przypisuje wartość true, jeśli zdefiniowany jest symbol winXPlub win2000oraz symbol releasejest zdefiniowany. Ostrzeżenie Należy zwracać uwagę, aby nie nadużywać dyrektyw kompilacji warunko wej i nie przesadzać ze złożonością wyrażeń warunkowych. W przeciwnym wypadku kod może się stać nieczytelny i trudny w zarządzaniu, zwłaszcza w wypadku rozbudowy. Mniej elastyczną, ale bardziej elegancką alternatywą dyrektywy preprocesora #if jest atry but System.Diagnostics.Conditiona!Attribute. Po zastosowaniu atrybutu Conditiona!Attribute do metody, kompilator zignoruje wszystkie wywołania tej metody, jeśli symbol określony przez Conditiona/Attribute nie jest zdefiniowany w punkcie wywołania. W podanym niżej przykładzie Conditiona/Attributeokreśla, że wywołania metody DumpState powinny być zawarte w skompilo wanej asemblacji tylko wtedy, kiedysymbol DEBUGzostał zdefiniowany podczas kompilacji. [ System . Diagnostics . Conditional ( "DEBUG" ) J public static void DumpState ( ) {//-} Użycie Conditiona/Attribute centralizuje logikę warunkowej kompilacji w deklaracji metody, co oznacza możliwość swobodnego umieszczania wywołań do metod warunkowych, bez potrzeby przepełniania kodu dyrektywami #if. Ponieważ kompilator po prostu usuwa wywo łania metody warunkowej z kodu, nie może on zawierać zależności od wartości zwracanych z metody warunkowej. Oznacza to, że Conditiona/Attribute można stosować tylko do metod, które zwracają stan pusty (void). Można zastosować wielokrotne instancje Conditiona/Attribute do metody, aby utworzyć zachowanie logiczne OR. Wywołania podanej niżej wersji metody DumpState będą skompilo wane tylko wtedy, gdy symbole DEBUG OR TESTsą zdefiniowane. [ System . Diagnostics . Conditional ( "DEBUG" ) J [System . Diagnostics . Conditional ( "TEST" ) J public static void DumpState( ) {//-} Osiągnięcie zachowania logicznego AND nie jest tak proste i wymaga użycia pośredniej metody warunkowej, co szybko prowadzi do nakładania się zapisów i w efekcie do złożonego, trudnego do zrozumienia i utrzymania kodu. Oto szybki przykład, wymagający zdefiniowania symboli DEBUGoraz TESTdla wywoływania funkcjonalności DumpState (zawartej w DumpState2). [ System . Diagnostics . Conditional ( " DEBUG" ) J public static void DumpState ( ) { DumpState2 { ) ; }
[System . Diagnostic s . Conditional ( "TEST" ) J public static void DumpState2 ( ) {li-} Rozdział 1 : Tworzenie aplikacji 15 Uwaga Klasy Debugi Trace, z przestrzeni nazw System.Diagnostics, używają Conditional Attributedla różnych metod. Metody klasy Debug są warunkowe na podstawie definicji sym bolu DEBUG, a metody klasy Trace są warunkowe na podstawie definicji symbolu TRACE. 1 .7 Dostęp do elementu programu o tej samej nazwie, co słowo kluczowe Problem Chcesz mieć dostęp do pola typu, ale nazwa typu lub nazwa pola jest taka sama, jak słowo kluczowe C# . Rozwiązanie Dołącz znak (@) jako prefiks do wszystkich instancji nazwy identyfikatora w swoim kodzie. Omówienie Platforma .Net Framework umożliwia użycie składowych oprogramowania, pochodzących z aplikacji języka C#, napisanych w innych językach .NET. Każdy język zawiera własny zestaw słów kluczowych (lub słów zarezerwowanych), co nakłada na programistę określone obostrzenia dotyczące nazw, które może przypisywać elementom programu, takim jak typy, pola i zmienne. Może się zdarzyć, że programista uruchamiający składową programu w innym języku, użyje niechcący słowa kluczowego języka C# jako nazwy elementu programu. Symbol @ umożliwia uniknięcie ewentualnego konfliktu, dotyczącego użycia słowa kluczowego C# jako identyfika tora. Poniższy fragment kodu nadaje instancję obiektowi typu operator (być może telefoniczny) i ustawia jego właściwość volatile (ulotność) na wartość true, przy czym zarówno operator jak volatile są słowami kluczowymi języka C#. li Tworzenie instancj i obiektu operato ra @ope rator Operatorl = new @operator( ) ; li Określanie właściwości volatile dla operatora Operatorl . @volatile = t rue;
16 C# - Księga przykładów 1 .8 Tworzenie i zarządzanie parami kluczy silnej nazwy Problem Chcesz utworzyć klucze publiczny i prywatny (parę kluczy), aby przy ich pomocy można było przypisać asemblacjom silne nazwy. Rozwiązanie Użyj narzędzia Strong Name (sn.exe) do wygenerowania pary kluczy i zapamiętaj je w pliku lub w zasobniku kluczy programu usług kryptograficznych CSP (Cryptographic Service Provider). Uwaga Program CSP jest elementem programu Win32 CryptoAPI, który umożliwia usługi w zakresie utajniania i odtajniania informacji oraz generowania podpisu cyfrowego. CSP oferuje również możliwości przechowywania kluczy, przy zastosowaniu mocnych narzędzi szyfrujących i możliwości zasobów systemu operacyjnego do zabezpieczenia jego zawartości. Dokładniejsze omówienie CSP i CryptoAPI wykracza poza zakres niniej szej książki. Po odnośne szczegółowe informacje na temat CryptoAPI należy sięgnąć do dokumentacji SOK. Omówienie Aby wygenerować nową parę kluczy i zapamiętać je w pliku o nazwie MyKeys.snk, należy wyko nać polecenie sn -k MyKeys.snk (.snk jest zwykle używanym rozszerzeniem dla plików zawiera jących klucze silnej nazwy). Wygenerowany plik zawiera obydwa klucze: publiczny i prywatny. Można obejrzeć klucz publiczny przy użyciu polecenia sn -tp MyKeys.snk, które wygeneruje informację wyjściową, podobną do przedstawionej niżej (w skróconej formie): Microsoft (R) . NET Framework Strong Name Utility Version 1 . 1 . 4322 . 573 Copyright ( C ) Mic rosoft Corporation 1998- 2002 . All rights reserved . Public key is 07020000002400005253413200040000010001002b4ef3c2bbd6478802b64d0dd3f2e7c65ee ; <$V E>6478802b63cb894a782f3aladbb46d3ee5ec5577e7dccc818937e964cbe997c 12076c19f2d7ad 179f15f7dccca6c6b72a Public key token is 2ald3326445fc02a Token klucza publicznego, przedstawiony przy końcu wydruku, to co najmniej 8-bajtowy skrót kryptograficzny (hash), obliczony na podstawie klucza publicznego. Ponieważ klucz publiczny jest tak długi, moduł .NET wykorzystuje token ze względu na jego spakowaną formę - zarówno przy wyświetlaniu nazwy, jak i w wypadku odwołań do danego klucza w innych asemblacjach. Ogólne omówienie skrótów kryptograficznych zawiera rozdział 14.