* Limbajul C sharp *
* ABC-doar *
*********************
*********************************
* autor RUSU MIRCEA AUREL VALER *
*********************************
*************************************
* MOTTO: *
* *
* "Festina lente" *
* *
* "Dans le doute,abtiens-toi" *
* *
*************************************
De ce C sharp:
Generalitati . . . . . . . . . . . . . . . . . . . . . . 1
Compilatorul C sharp (optiuni) . . . . . . . . . . . . . 5
Limbajul C sharp . . . . . . . . . . . . . . . . . . . 13
Tipuri de data . . . . . . . . . . . . . . . . . . 13
Operatori . . . . . . . . . . . . . . . . . . . . . 21
Instructiuni . . . . . . . . . . . . . . . . . . . 23
Clase si obiecte . . . . . . . . . . . . . . . . . 25
Structuri . . . . . . . . . . . . . . . . . . . . . 35
Interfete . . . . . . . . . . . . . . . . . . . . . 38
Clasele de tip Delegates . . . . . . . . . . . . . 40
Indexatori (indexers) . . . . . . . . . . . . . . . 43
Generice . . . . . . . . . . . . . . . . . . . . . .44
Enumerari . . . . . . . . . . . . . . . . . . . . . 49
Iteratori . . . . . . . . . . . . . . . . . . . . . 51
Selectii LINQ . . . . . . . . . . . . . . . . . . . 55
Namespaces ( blocuri de memorie ) . . . . . . . . . 59
Nullable types . . . . . . . . . . . . . . . . . . .61
Unsafe code . . . . . . . . . . . . . . . . . . . . 63
Documentatia XML . . . . . . . . . . . . . . . . . 65
Structura si executia aplicatiilor . . . . . . . . . . .67
Application Domain . . . . . . . . . . . . . . . . .67
Atribute . . . . . . . . . . . . . . . . . . . . . 69
Containere de tip Collection . . . . . . . . . . . 72
Exceptii si erori . . . . . . . . . . . . . . . . . 76
Threads (procesarea paralela) . . . . . . . . . . . 79
Reflexie . . . . . . . . . . . . . . . . . . . . . .84
Securitatea Aplicatiilor . . . . . . . . . . . . . 87
Platforma .NET . . . . . . . . . . . . . . . . . . . . 91
Prezentare generala . . . . . . . . . . . . . . . 91
CLR (Common Language Runtime) . . . . . . . . . . 92
Class Library . . . . . . . . . . . . . . . . . . 93
System.Windows.Forms . . . . . . . . . . . . . . . 94
System.Data . . . . . . . . . . . . . . . . . . . 103
System.Timers . . . . . . . . . . . . . . . . . . 107
System.Runtime . . . . . . . . . . . . . . . . . . 108
System.Media . . . . . . . . . . . . . . . . . . . 109
System.IO . . . . . . . . . . . . . . . . . . . . 110
System.Diagnostics . . . . . . . . . . . . . . . . 112
Platforma Microsoft Visual C# 2008 . . . . . . . . . . 115
Windows Forms Application . . . . . . . . . . . . . 117
Exemplu OpenFile . . . . . . . . . . . . . . . . . 118
Exemplu Album Foto . . . . . . . . . . . . . . . . 121
Exemplu SoundPlayer . . . . . . . . . . . . . . . 124
Exemplu MediaPlayer . . . . . . . . . . . . . . . 126
Exemplu ChartFX . . . . . . . . . . . . . . . . . 128
Exemplu TextToHTML Converter . . . . . . . . . . . 129
Exemplu Web Browser . . . . . . . . . . . . . . . 130
Exemplu cu baza de date . . . . . . . . . . . . . 131
Exemplu cu PerformanceCounter . . . . . . . . . . 133
Windows Presentation Foundation . . . . . . . . . . 134
Exemple Grafica 2D . . . . . . . . . . . . . . . . 135
Exemplu Diagrama . . . . . . . . . . . . . . . . . 138
Exemple Grafica 3D . . . . . . . . . . . . . . . . 139
Exemple Animatie . . . . . . . . . . . . . . . . . 145
Exemplu Grid . . . . . . . . . . . . . . . . . . . 149
Exemplu Data Binding . . . . . . . . . . . . . . . 150
Exemplu cu baza de date . . . . . . . . . . . . . 152
Exemplu pentru documente XPS . . . . . . . . . . . 153
WPF Browser Application . . . . . . . . . . . . . . 157
Exemplu pentru WPFBrowser . . . . . . . . . . . . 157
Aplicatii cu ferestre multiple . . . . . . . . . . . 159
Concluzii generale,recomandari . . . . . . . . . . . . 160
GENERALITATI
-2-
7.Programarea automata -este conceputa ca un set de stari finite ce pot
fi controlate cu ajutorul unor variabile de control sau a unor tabele
de schimbare de stare.Cu alte cuvinte,programul este format din mai
multe module automate sau semi-automate.
EXEMPLE: Limbajul Basic este imperativ,Limbajul Pascal este structurat si
functional,Limbajul C este pur functional,Limbajul Java este procedural si
"event-driven" iar platforma Visual Studio este formata exclusiv din
limbaje orienate spre obiect.
Se spune despre C sharp ca este multiparadigma,tocmai pentru ca accepta
toate aceste stiluri de programare,pentru a crea solutii personalizate,in
functie de necesitatile de moment.Cu alte cuvinte,daca este nevoie de o
singura linie de comanda,nu trebuie creat un obiect,sau o fereastra de
control ci se poate implementa direct comanda respectiva.Se va putea
construi un modul executabil ce contine o singura comanda.
Exemplu: -deschide automat un site de Web.
Aplicatiile C sharp,consuma minimum de memorie necesara,dar economia de
memorie nu este scopul principal pentru acest limbaj.Mai degraba,pentru
a simplifica munca programatorului,compilatorul C sharp include si un
mecanism de tip "automatic garbage collection",asemanator cu cel din Java,
prin care vaiabilele si constantele inutile sunt eliberate automat din
memorie,in functie de vechimea lor in program.
Compilatorul verifica foarte strict tipurile de data,dimensiunea
ariilor de date si variabilele neinitializate sau desuete,pentru a limita
la maximum procesarea de date "defective",sau imposibil de gasit (procese
redundante).
Aplicatiile scrise in limbaj C#,sunt portabile pe numeroase sisteme de
operare si sunt compatibile cu orice alt program ce utilizeaza standardul
CLI (Common Language Infrastructure) specificat prin documentul ECMA-335.
Standardul CLI propune un set de reguli cunoscut sub numele de CLS (Common
Language Specification) cu scopul de a asigura interoperabilitatea dintre
mai multe limbaje de programare.Codurile care respecta aceste reguli de
limbaj,sunt arhivate in niste biblioteci speciale si poarta numele de
"framework" (suport).O astfel de biblioteca este comuna pentru mai multe
limbaje de programare.Concret,limbajul C# exploateaza biblioteca .Net
(mai exact Microsoft.Net).Aceeasi biblioteca este exploatata si de catre
platforma Visual Basic 2010 si intregul set de programe din platforma
Visual Studio 2010.Ca rezultat,programele editate in Visual C# vor putea
fi portabile si pentru Visual Basic 2010.Mai mult decat atat,utilizatorii
platformei Visual Studio 2010 vor putea construi aplicatii ce utilizeaza
module scrise in limbaje diferite.Fiecare astfel de modul,va fi un simplu
executabil ce poate fi apelat la fel ca si o functie din program.
Numele de C sharp (C#) provine de la semnul "diez" din muzica si
indica faptul ca este vorba despre o incrementare a limbajului C.A fost
propusa initial denumirea de C++++,pentru a simboliza faptul ca este o
incrementare a limbajului C++,dar din motive comerciale s-a ales notatia
C#.Platforma .Net contine si alte limbaje denumite asemanator: J#,A#,Gtk#,
Cocoa# sau Qt#,toate cu scopul de a simboliza o incrementare a limbajului
de baza.Totusi,C# nu este un C++ cu functii auxiliare ci este un program
complet nou,cu alta biblioteca de functii si alta conceptie de organizare.
Nici compilarea si constructia aplicatiilor nu este similara.C sharp
construieste direct executabile,pentru sistemul de operare instalat.
-3-
Mai exact,compilatorul C# lucreaza in doua etape.In prima etapa codul
nativ este convertit la un limbaj intermediar: CIL (Common Intermediate
Language).In cea de a doua etapa,codul intermediar este transformat in
cod masina,in functie de sistemul de operare instalat.Acest mecanism este
asemanator cu cel al compilatorului Java.Principalul avantaj este dat de
faptul ca acelasi fragment de cod nativ,poate fi transformat in executabil
pentru Windows,Linux sau alt sistem de operare.Daca se creaza un sistem de
operare nou,nu va trebui rescris decat compilatorul.Toate aplicatiile
vechi vor putea fi utilizate pentru noul sistem de operare.Mai mult decat
atat,toate limbajele care exploateaza platforma .Net,utilizeaza acelasi
cod intermediar.Ca rezultat,aplicatiile pot utiliza module scrise in
limbaje de programare diferite.
Platforma .Net revolutioneaza si modul de arhivare a resurselor si
a bibliotecilor de coduri.Vechiul sistem utiliza celebrele biblioteci cu
alocare dinamica (DLL).Sistemul a fost exceptional la inceput,dar numarul
extrem de mare de file DLL noi,sau de versiuni incompatibile,a facut ca
gestionarea acestui tip de resurse sa fie tot mai dificila.In numeroase
cazuri,programele instalau versiuni diferite ale unei file DLL,versiuni
incompatibile cu restul programelor ce exploatatu aceasi fila DLL,avand
ca rezultat defectarea acestor programe.In plus,numeroasele file DLL,
puteau introduce o serie de conflicte de identificator,de supraincarcari
ale functiilor si claselor...etc.
Pentru a solutiona aceste inconveniente,platforma .Net a introdus un
nou tip de unitate logica pentru arhivarea resurselor,denumit "asamblare"
(assemblies).Aceste unitati pot fi de doua tipuri: aplicatii cu extensia
.exe (numite frecvent si executabile) si componente cu extensia .dll.
Primele sunt programe complete,executabile direct,iar ultimele sunt doar
obiecte,ce pot fi asamblate pentru a crea aplicatii noi.Din acest motiv,
in cazul limbajului C# se prefera expresia de "limbaj orientat spre
component" in locul celei de "limbaj orienat spre obiect".Deosebirile nu
sunt foarte mari,dar exista.Pentru robustetea suportului,limbajul C# nu
accepta mostenirea multipla.Fiecare obiect trebuie sa fie derivat din o
singura clasa ancestoare.Pentru a personaliza obiectul se pot utiliza
insa un numar nelimitat de interfete.
O astfel de unitate,denumita "asamblare" este mai complexa decat o
simpla arhiva pentru codul sursa si contine:1.-O serie de metadate in care
sunt arhivate informatii despre unitate,modul de acces si o lista de file
dependente (se mai numeste si "manifest") 2.-O serie de metadate in care
sunt descrise tipurile de data utilizate 3.-Codul intermediar propriu zis
(Codul MSIL) 4.-Resursele necesare.
Asamblarile pot fi de doua tipuri: private si partajate.Cele private
pot fi utilizate doar de catre aplicatia care le-a instalat,iar cele
partajate,pot fi utilizate in retea,de catre doi sau mai multi utilizatori
simultan.In majoritatea situatiilor,se vor prefera cele private,deoarece
sunt usor de realizat,sunt portabile,pot fi instalate in acelasi director
cu aplicatia,nu se preteaza la interactiuni cu structuri asemanatoare.
Cele partajate,se creaza mai dificil,cu instrumente specializate si
trebuie sa primeasca un cod criptografic unic,pentru a evita orice fel
de conflicte de identificator.In schimb,cele partajate pot fi utilizate
pentru a construi aplicatii noi,sau pot fi apelate din retea de la
locatii diferite.
-4-
O astfel de asamblare poate fi o fila unica,sau poate fi formata din
mai multe file,caz in care exista o fila centrala ce contine manifestul,
metadatele si link-urile spre restul filelor.
Cu alte cuvinte,platforma .Net reprezinta o noua etapa de dezvoltare
pentru relatia dintre aplicatie si resurse.Dupa cum anunta si numele,
suportul .Net este destinat mai ales pentru a crea aplicatii portabile,
de tip Web Forms sau pentru a facilita schimbul de date in retea,fie
cu ajutorul unor baze de date,fie cu alt tip de structuri de memorie.Dar
posibilitatile nu se rezuma la aplicatii de tip Web.Limbajul C# si
platforma .Net se pot combina pentru a obtine orice tip posibil de resursa
software.Initial a purtat numele de NGWS(Next Generation Windows Services)
pentru a sublinia faptul ca este vorba despre o tehnologie noua.Suportul
.Net are la baza urmatoarele standarde Internet: HTTP,XML,SOAP si UDDI.
Dintre trasaturile caracteristice limbajului C#,merita mentionat:
1. Nu exista functii sau variabile globale.Toate datele trebuie sa fie
declarate in interiorul unor clase.Membrii statici ai unei clase publice
pot substitui cu succes functiile si variabilele globale.
2. Tipul de data boolean este strict.Conditiile de tip while sau if vor
evalua strict date de tip boolean (True sau False),spre deosebire de
limbajul C++ unde sunt acceptate si valori de tip INT,ce pot fi convertite
la valori booleene (0 si 1).
3. C# nu lucreaza cu pointeri spre adrese de memorie,ci doar cu referinte
spre obiecte reale.Daca se utilizeaza pointeri,blocurile de cod vor fi
marcate ca nesigure (unsafe) si nu vor putea fi executate decat dupa ce
se solicita permisiunea utilizatorului.
4. Memoria nu poate fi eliberata explicit.Toate operatiile de eliberare
a memoriei se fac automat,prin "Garbage collection".
5. Pe linga try...catch,C# poate contine si bucle de tip try...finaly,
pentru a gestiona functiile de tratare a exceptiilor.
6. C# nu accepta mostenirea multipla,dar accepta oricate interfete.
7. Limbajul C# este foarte strict in ce priveste tipul de data.Nu se fac
nici un fel de conversii automate (cu exceptia extinderii tipului INT).
8. Limbajul C# unifica toate tipurile de data intr-un sistem comun denumit
CTS (Common Type System).Ca rezultat,toate tipurile de data,inclusiv cele
primitive (exemplu INT) sunt subclase ale clasei radacina: System.object.
Astfel mostenesc metodele clasei de baza (Exemplu: toate tipurile de data
vor avea si metoda: ToString()).
Dintre principalele facilitati noi,oferite de combinatia C# si .Net,
merita remarcate urmatoarele: independenta fata de platforma software,
interoperabilitatea cu alte limbaje,multi-threading,conectarea la serverul
SQL 2005,suportul pentru formate de 64 de biti,genericele,metodele anonime
si clasele partiale,iteratorii si delegatii (delegates).
Pentru a edita programele C#,exista doua solutii:
1.-editati codul cu orice editor de text,salvati fila cu extensia .cs si
apoi compilati fila cu utilitarul csc.exe,pentru a crea un executabil.
2.-utilizati una dintre versiunile de Visual C#.
Ambele metode au avantaje si dezavantaje.Prima metoda,se poate aplica
si in absenta unei conexiuni la Internet,ofera programe mici,simple si
robuste in care trebuie sa controlati fiecare linie de cod.Cea de a doua
metoda,este mult mai eleganta si comoda,permite construirea unor aplicatii
complexe si beneficiati de un numar foarte mare de instrumente automate.
-5-
COMPILATORUL C Sharp
-6-
Un alt program minimal poate deschide si actualiza o fila de tip text.
Exemplu: -Salvati in directorul C o fila goala denumita test.txt,apoi
editati urmatorul cod:
using System;
using System.IO;
class WelcomeCSS {
static void Main() {
string lines = "Prima linie.\r\n A doua linie.\r\n Ultima linie.";
System.IO.StreamWriter file = new System.Io.StreamWriter("c:\\test.txt");
file.WriteLine(lines);
file.Close();
Console.WriteLine("Textul a fost editat in fila test.txt");
Console.ReadLine();
}
}
Salvati fila cu numele Test1.cs,apoi compilati cu o comanda:
csc.exe Test1.cs
Executati un dublu click pe executabilul Test1.exe si apoi verificati
daca textul a fost editat in fila de control (test.txt).
Observati ca structura programului este asemanatoare cu C++.
Bibliotecile se importa cu "using" in loc de "import".Intregul program
trebuie sa fie inclus intr-o clasa,iar fluxul de executie trebuie
controlat cu ajutorul unei functii principale,denumita functia Main().
Pentru a intelege mai bune functiile apelate,deschideti MSDN Library si
apoi explorati biblioteca System si respectiv System.IO.
Daca exista date si functii cu care lucrati frecvent,este comod si
practic sa arhivati aceste functii intr-o biblioteca DLL proprie,pe
care o puteti apoi apela din programe diferite.Pentru acest scop,trebuie
sa construiti o asamblare de tip arhiva (DLL) cu acces privat.
EXEMPLU: pentru o operatie de adunare,editati modulul:
using System;
namespace FunctiileMele
{
public class Adunare
{
public static long Add(long i,long j)
{
return(i+j);
}
}
}
Salvati fila cu numele Adunare.cs.
pentru o operatie de inmultire,editati un modul de genul:
using System;
namespace FunctiileMele {
public class Inmultire {
public static long Multiply(long x,long y) { return (x*y); }
}
}
Salvati fila cu numele Produs.cs.Observati ca ambele file declara si
un spatiu denumit (namespace) ce va fi utilizat ca identificator.
-7-
In continuare,cele doua functii pot fi incluse intr-o fila DLL cu o
astfel de comanda:
csc /target:library /out:FunctiileMele.DLL Adunare.cs Produs.cs
Observati ca in directorul curent a fost asamblata fila:
FunctiileMele.DLL 0.0.0.0
In continuare,aceasta biblioteca va putea fi apelata la fel ca orice
alta biblioteca DLL.Sa presupunem ca doriti sa scrieti un mic modul,in
care se aplica ambele functii,pentru doi parametri inclusi in linia de
comanda.Editati un program de genul:
using System;
using FunctiileMele;
class Operatie1 {
public static void Main( string[] args) {
if (args.Length != 2) {
Console.WriteLine("Comanda este: Operatie1 <num1><num2>");
return;
}
long num1 = long.Parse(args[0]);
long num2 = long.Parse(args[1]);
long sum = Adunare.Add(num1,num2);
long product = Inmultire.Multiply(num1,num2);
Console.WriteLine("Suma dintre {0} si {1} este {2}",
num1,num2,sum);
Console.WriteLine("Produsul dintre {0} si {1} este {2}",
num1,num2,product);
}
}
Salvati fila cu numele Operatie1.cs.Pentru a produce un executabil se
va include in linia de comanda si resursa DLL utilizata,astfel:
csc /out:Operatie1.exe /reference:FunctiileMele.DLL Operatie1.cs
In continuare,executabilul Operatie1.exe,poate fi utilizat direct cu
o linie de comanda,in care se introduc si valorile ce urmeaza sa fie
calculate.
Exemplu: pentru numerele 111 si 22222 se va utiliza o comanda:
Operatie1 111 22222
Programul va returna mesajele:
Suma dintre 111 si 22222 este 22333
Produsul dintre 111 si 22222 este 2466642
Asadar compilatorul csc.exe poate produce atat executabile cat si
biblioteci DLL,in functie de optiunile din linia de comanda.Pentru a
vedea toate optiunile posibile,puteti edita o comanda de genul:
csc.exe / ? sau csc.exe / help
Acest manual nu va analiza decat succint optiunile de compilare,asa
ca este recomandabil sa studiati referinta MSDN si sa analizati cu
maximum de atentie posibilitatile oferite de compilator.O mare parte
dintre optiuni nu sunt accesibile din interfata Visual C# si nu pot fi
apelate decat cu ajutorul unei linii de comanda.
O linie de comanda poate sa includa o singura optiune,sau o combinatie
de optiuni,in functie de rezultatul scontat.Puteti copia soltii de
compilare,din manuale si tutoriale,dar este preferabil sa analizati si sa
intelegeti continutul unei astfel de comenzi.
-8-
PRINCIPALELE OPTIUNI DE COMPILARE SUNT:
-9-
Cu alte cuvinte,modulele se pot utiliza in lista de file sursa,
pentru a crea executabile noi.
Varianta /target:winexe creaza executabile de tip Windows.Si
acestea vor avea tot extensia .exe,dar vor avea o interfata atat
pentru platforma .Net Framework cat si pentru Win32 API.
EXEMPLU: csc /target:winexe test.cs
va crea un program de tip Windows.
/addmodule:file[;file2] -este optiunea prin care se pot include in etapa
de asamblare si module create anterior cu /target:module.Nu se
pot include file complete ce contin si manifestul.Pentru a
include simultan mai multe module,se poate utiliza virgula sau
punct si virgula intre module.Toate modulele utilizate la
constructie trebuie sa insoteasca fila executabila in momentul
executiei (sa fie in acelasi director cu fila executabila),la
fel ca orice alta fila de resurse.
EXEMPLU: csc/addmodule:metad1.netmodule /out:Program1.exe test.cs
In exemplul de mai sus,compilatorul va adauga la asamblare si
modulul meta1.netmodule,pe langa fila sursa test.cs
/lib - permite specificarea unui director,sau a unei liste de directoare,
in care compilatorul va cauta preferential o resursa,atunci cand
nu este in acelasi director cu compilatorul.
EXEMPLU: csc /lib:c:\ /reference:t2.dll t2.cs
In exemplul de mai sus,pentru a construi executabilul,compilatorul
va cauta fila t2.dll in directorul curent,sau in directorul C:\
/nostdlib[+ sau -] - permite sau blocheaza importul automat al bibliotecii
mscorlib.dll (cea care defineste System namespace).Implicit,se va
utiliza optiunea /nostlib+.Are rost sa specificati /nostlib-,doar
atunci cand doriti sa creati un System namespace propriu si
aplicatia urmeaza sa fie executata in mediu minim de memorie.
/reference:file[;file2] - se utilizeaza pentru a importa una sau mai
multe biblioteci DLL,necesare pentru compilare.Daca sunt mai
multe,se vor specifica sub forma de lista,separate prin punct si
virgula.Optiunea se poate scrie prescurtat si /r.
EXEMPLU: csc /r:meta1.dll;meta2.dll /out: Program.exe test.cs
In exemplul de mai sus,bibliotecile meta1 si meta2 se vor compila
impreuna cu fila sursa test.cs pentru a produce fila Program.exe.
/bugreport:file -permite crearea unei file de depanare,in care se vor
edita automat urmatoarele date:
-o copie a tuturor filelor sursa
-o lista cu optiunile de compilare
-informatii despre versiunea compilatorului,sistemul de operare si
mediul de executie.
-o lista a bibliotecilor DLL si a modulelor utilizate,altele decat
cele din suportul .NET.
-datele produse de compilator (daca exista)
-o scurta descriere a problemei (editata de utilizator)
-o scurta propunere de remediere (editata de utilizator)
EXEMPLU: csc /bugreport:problema.txt Test.cs
In exemplul de mai sus,se va crea automat fila problema.txt in
care se vor arhiva toate elementele amintite mai sus.Puteti apoi
expedia aceasta fila la compania Microsoft.
-10-
/checked[+ sau -] -verifica daca valorile aritmetice de tip integer sunt
sau nu sunt cuprinse in domeniul de valori (Overfolw/underflow).
Daca optiunea este checked+ si o valoare este situata in afara
domeniului,se va genera o eroare de executie.Daca se compileaza
codul cu optiunea checked-,instructiunea nu va genera o eroare de
executie,chiar daca exista valori INT in afara domeniului.
/debug[+ sau -] si /debug:{full sau pdbonly} -se utilizeaza pentru a
genera o fila in format .pdb (program database) in care se vor
arhiva informatii utile pentru depanare.Implicit este /debug-.
Daca se utilizeaza /debug+ se poate alege una dintre variantele
full sau pdbonly (implicit este full).
EXEMPLU: csc /debug test.cs.
/fullpaths -in mod implicit,daca exista erori,compilatorul specifica fila
in care a intervenit eroarea.Daca se utilizeaza optiunea /fullpaths,
compilatorul va indica si calea completa de acces la fila in care a
intervenit eroarea.
/warn:option -permite alegerea nivelului de avertismente emise de catre
compilator.Pentru option se pot utiliza urmatoarele valori:
0 -blocheaza toate mesajele de avertizare
1 -afiseaza doar avertismentele foarte severe
2 -afiseaza nivelul 1 de avertismente si unele mai putin severe
3 -afiseaza nivelul 2 de avertismente plus avertismente banale
4 -afiseaza tot setul de avertismente.Este optiunea implicita.
EXEMPLU: csc /warn:1 test.cs
/warnaserror[+ sau -] -trateaza toate avertismentele ca pe niste erori de
compilare si blocheaza asamblarea filei solicitate,pana dupa ce se
remediaza toate aceste erori.Implicit se utilizeaza: warnaserror-.
/define:name[;name2] -este echivalentul unei directive de preprocesare
definita prin #define.Se utilizeaza pentru a defini simbolurile
specificate prin "name" in etapa de preprocesare a codului.Acestea
vor ramane definite pana la o comanda #undefine.Se pot utiliza si
bucle conditionale: #if,#else,#elif si #endif.Se poate utiliza si
varianta prescurtata /d.Daca se definesc mai multe simboluri,acestea
se vor separa prin virgula sau punct si virgula.
EXEMPLU: editati urmatoarea fila test.cs:
using System;
public class Test {
public static void Main(){
#if (xx)
Console.WriteLine("xx exista");
#else
Console.WriteLine("xx nu exista");
#endif
Console.ReadLine();
}}
Compilati fila cu: csc /define:xx test.cs si apoi cu
csc test.cs si observati diferenta.
Directivele de precompilare se pot utiliza atunci cand doriti sa
blocati compilarea unor fragmente de cod.Exemplu: doriti sa puteti
produce versiuni diferite ale programului: o versiune trunchiata si
o versiune completa.
-11-
/linkresource:filename[,identifier] - creaza un link spre una dintre
resursele suportului .NET.Fila de resurse nu va fi amplasata in
fila de output (se creaza doar link-ul).Pentru a include fila se
poate utiliza optiunea /resource.Nu se poate utiliza pentru module.
Prin "filename" se specifica resursa,iar prin "identifier" se va
specifica numele resursei utilizat pentru import(de obicei numele
filei ce contine resursa).
EXEMPLU: csc /linkresource:rf.resource test.cs
/resource:filename[,identifier] -se utilizeaza pentru a ingloba o fila de
resurse in fila asamblata.Prin "filename" se specifica resursa,iar
prin "identifier" numele resursei (numele filei ce contine resursa).
EXEMPLU: csc /resource:rf.resource test.cs
/win32icon:filename -se utilizeaza pentru a include o fila de tip icon in
fila asamblata,astfel incat executabilul sa fie afisat pe ecran cu
aspectul dorit.Fila tip .ico poate fi creata si dintr-o fila .rc
cu Resource Compiler (dar mai simplu se creaza in Paint).
EXEMPLU: csc /win32icon:rf.ico test.cs
/win32res:filename - se utilizeaza pentru a insera o rsursa de tip Win32
in fila de asamblare.Fila de resurse .res poate fi creata si din
fila .rc cu Resource Compiler.
EXEMPLU: csc /win32res:rf.res test.cs
@file_name -se utilizeaza pentru a prelua comanda de compilare dintr-o
fila cu extensia .rsp.Se utilizeaza atunci cand asamblati frecvent
file cu o comanda de compilare mai complexa.Eventual se pot utiliza
doua sau mai multe astfel de file sursa,specificate fiecare separat:
@fila1.rsp @fila2.rsp ..etc.
EXEMPLU: -deschideti Notepad si editati textul:
/target:exe test.cs
salvati fila cu numele "fila1.rsp" (cu numele intre
ghilimele-altfel se va salva cu extensia .txt).Apoi puteti utiliza
fila cu o comanda: csc @fila1.rsc
/help si /? -se utilizeza pentru a lista optiunile de compilare si o
scurta descriere a lor.Nu se asambleaza nici o fila.Este doar pentru
o orientare rapida,inainte de compilare.
Exemplu: csc /? sau csc /help
/baseaddress:address -se utilizeaza pentru a specifica o adresa de baza
preferata,pentru importul unei file .DLL.Adresa se poate specifica
sub forma de numar zecimal,octal sau hexazecimal.La import,adresa
va fi rotunjita (Exemplu: 0x11110001 devine 0x11110000).
/codepage:id -se utilizeaza atunci cand fila de cod compilata utilizeza
un alt set de caractere decat cel instalat in sistemul de operare
si altul decat UTF-8.
/incremental[+ sau -] -se utilizeaza atunci cand doriti sa compilati
doar actualizarile unei file.Pentru acest scop,trebuie activata
compilarea incrementala.Prima data cind utilizati optiunea
/incremental,se va crea o fila .incr in care se salveaza informatii
despre statusul compilarii,altele decat cele arhivate in fila de
debug .pdb.Daca schimbati optiunile de compilare cu o noua optiune
/incremental,fiele .incr si .pdb vor fi actualizate automat.La o
astfel de compilare,optiunea /doc este ignorata.Este utila atunci
cand asamblarea se face din foarte multe file mici.
-12-
/main:class -se utilizeaza atunci cand codul sursa contine mai multe
clase in care este definita o functie Main(),pentru a specifica
explicit care este functia Main() din care incepe executia
programului (functia controller).
EXEMPLU: csc t2.cs t3.cs /main:Test2
In exemplul de mai sus,functia Main() de lansare a programului va
fi cea definita in clasa Test2.
/noconfig -se utilizeaza pentru a instrui compilatorul sa ignore fila
csc.rsp.
/nologo -se utilizeaza pentru a impideca afisarea unui banner in timp ce
se executa compilarea.
/recurse:[dir\]file - se utilizeaza pentru a putea compila si file sursa
localizate la alta adresa decat in directorul curent.Prin "dir" se
specifica directorul,iar prin "file" fila sau filele sursa.Se pot
utiliza si caractere generale,pentru a selecta toate fielele ce
corespund unui anumit pattern (Exemplu: csc *.cs).
EXEMPLU:
csc /target:library /out:bib1.DLL /recurse: dir1\dir2\*.cs
In exemplul de mai sus,se vor compila toate filele din directorul
dir2,arhivat in dir1,pentru a forma fila: bib1.DLL.
/unsafe -se utilizeaza pentru a permite compilarea si atunci cand exista
coduri marcate ca nesigure.
EXEMPLU: csc /unsafe test.cs
/utf8output -se utilizeaza pentru ca fila de output sa fie codificata in
caractere de tip UTF8.
-15-
Exista numeroase alte solutii pentru a exploata datele procesate in
program.Exemple: fluxul de iesire poate fi salvat intr-o fila auxiliara,
intr-un tabel sau intr-o baza de date,poate fi expediat prin e-mail,sau
poate fi arhivat intr-un tampon temporar de memorie,poate fi expediat la
o adresa de Internet sau spre un telefon mobil...etc.Totusi,cele mai
multe aplicatii,afiseaza datele in Consola,sau intr-o fereastra Windows.
Compilatorul C# verifica extrem de sever formatul datelor si domeniul
de reprezentare.Din acest motiv,se spune despre C# ca este un limbaj
puternic tipizat.Spre deosebire de Delphi sau PHP,limbajul C# nu contine
nici un tip de data "variabila" si nu executa nici un fel de conversii
automate.Din acest motiv,tipul de data trebuie ales cu mare atentie,
inainte de a incepe codificarea.Exista si conversii naturale,ce se pot
executa direct.Exemplu: valorile de tip "int" pot fi convertitie la tipul
"long" fara riscul de a pierde date.In schimb,daca se face conversia
inversa: din "long" in "int",exista riscul de a pierde toate datele ce
depasesc domeniul de reprezentare "int".
EXEMPLU: long 2147483649 devine int -1 (deoarece int max= 2147483648)
Pentru majoritatea conversiilor se poate utiliza obiectul Convert.
EXEMPLU:
using System;
class Date1 {
public static void Main() {
Console.BackgroundColor = ConsoleColor.Yellow;
Console.ForegroundColor = ConsoleColor.Blue;
-16-
Aceleasi solutii sunt necesare si pentru a prezenta datele intr-o
fereastra Windows.Fie ca se apeleaza la componente de tip Label,fie ca
se afiseaza datele cu DrawString(),valorile numerice trebuie sa fie
convertite initial la valori de tip "string".
EXEMPLU:
using System;
using System.Drawing;
using System.Windows.Forms;
-17-
Chiar daca sunt obiecte distincte,tipurile de data predefinite sunt
importate din biblioteca System si au vizibiliate globala.Daca se creaza
un obiect derivat dintr-un tip de data,procesul poarta numele de "boxing"
(impachetare) iar daca se creaza un tip numeric dintr-un astfel de obiect,
procesul poarta numele de "unboxing" (despachetare).
EXEMPLU: int i = 123;
object numar1 = i; // boxing
int x = (int) numar1; // unboxing
Impachetarea sau despachetarea unui tip numeric,are rost fie pentru
a izola datele intr-un anumit spatiu de vizibiliate,fie pentru a adauga
un set nou de metode,sau dimpotriva pentru a elimina un set de metode
supraincarcate.
EXEMPLU: daca se lucreaza cu o stiva de obiecte,este esential ca datele
numerice sa fie impachetate,inainte de a putea fi introduse in stiva.
Dintre tipurile referinta,se va exemplifica in acest capitol doar tipul
arie (restul sunt prezentate in capitole separate).Tipul arie este un tip
complex,extrem de util,ce poate inlocui cu succes tabelele si bazele de
date,atunci cand volumele de date sunt mici.Tot ariile se utilizeaza si
pentru a prelua segmente de date,din tabele si baze de date.Ariile pot
fi uni,bi sau multidimensionale.Pentru operatiile cu arii,C# utilizeaza
obiectul Array (vezi referinta).Una dintre avantajele majore oferite de
arii,este sortarea automata,in functie de indexul numeric,sau in functie
de un cod cheie,utilizat pentru organizarea ariei.
EXEMPLU:
using System;
using System.Collections;
public class ExempluArie {
public static void Main() {
String[] myKeys = { "Rosu","Verde","Galben","Albastru"};
String[] myValues = {"Capsuni","Pere","Lamai","Afine"};
Console.WriteLine("Aria initiala:");
Scrie(myKeys,myValues);
Array.Sort(myKeys,myValues);
Console.WriteLine("Dupa sortare: ");
Scrie(myKeys,myValues);
Console.ReadLine();
}
public static void Scrie( String[] myKeys,String[] myValues){
for (int i=0;i<myKeys.Length;i++) {
Console.WriteLine(" {0,-20}:{1}",myKeys[i],myValues[i]);
}
Console.WriteLine();
}
}
Salvati fila cu numele Consola4.cs si compilati.
In exemplul de mai sus am creat doua arii cu elemente de tip string,
apoi am sortat elementele cu ajutorul cheii de indexare myKey.Pentru a
citi si edita elementele din arie,am definit metoda Scrie,in care fiecare
element din arie este citit si editat in ordinea de indexare dictata de
myKeys.Acest gen de operatii sunt extrem de utile,atunci cand aria contine
sute sau zeci de inregistrari,imposibil de evaluat direct.
-18-
O alta facilitate oferita de arii,este optiunea de a cauta rapid un
anumit obiect cu ajutorul metodei BinarySearch().
EXEMPLU:
using System;
public class ExempluArie {
public static void Main() {
Array a1 = Array.CreateInstance(typeof(Int32),5);
a1.SetValue(8,0);
a1.SetValue(2,1);
a1.SetValue(6,2);
a1.SetValue(3,3);
a1.SetValue(7,4);
Array.Sort(a1);
object.obj1 = 6;
Cauta(a1,obj1);
Console.ReadLine();
}
public static void Cauta(Array myArr,object obj2)
{ int ind1=Array.BinarySearch(myArr,obj2);
Console.WriteLine("Obiectul ({0}) este la index {1}.",ind1,obj2);
}
}
Salvati fila cu numele Consola5.cs si compilati.
In exemplul de mai sus,am creat o arie numerica formata din 5 elemente in
care numarul de indexare este de tip Int32.In acest mod,aria va putea fi
extinsa pana la peste 2 miliarde de inregistrari.Apoi am introdus primele
5 valori,impreuna cu cheia lor de indexare numerica.Dupa sortare,toate
elementele si-au schimbat pozitia din arie.Pentru a gasi un anumit
element,am scris metoda Cauta(),ce are la baza metoda Array.BinarySearch()
si apoi am identificat noua pozitie a elementului dorit.Acest mecanism
este extrem de util,atunci cand aria este multidimensionala si contine
mii de inregistrari asemanatoare.
Ariile pot fi incluse una in alta,pentru a crea arii intricate,
denumite si "jagged array" (arii in zig-zag).
EXEMPLU: class Test {
static int void Main() {
int[] a1 = new int[] { 1,2,3};
int[,] a2 = new int[,] {{1,2,3},{4,5,6}};
int[,,] a3 = new int[10,20,30];
int[][] j2 = new int[3][];
j2[0] = new int[] {1,2,3};
j2[1] = new int[] {1,2,3,4,5,6};
j2[2] = new int[] {1,2,3,4,5,6,7,8,9};
}}
In exemplul de mai sus,j2 este o "arie de arii".
Pentru fiecare metoda,obiectul Array contine seturi alternative de
metode supraincarcate,cu diferiti parametri sau cu solutii diferite de
procesare.Este bine sa studiati cu atentie toate aceste optiuni,inainte
de a defini o metoda noua.Metodele standardizate,predefinite,sunt mai
simple si mai sigure decat cele improvizate la moment.
-19-
Probabil ca tipul de data cel mai frecvent utilizat,este tipul "string".
Din acest motiv,obiectul System.String contine un set foarte bogat de
metode,dintre care cele mai frecvent utilizate sunt: Compare,Concat,Format
Indexof,Insert,Join,Replace,Split,Substring,ToLower,ToUpper,Trim.
Cu ajutorul acestor metode,se pot excuta toate tipurile posibile de
operatii,cu si asupra datelor de tip caracter.
Cea mai simpla operatie este separarea unui text in cuvinte,cu ajutorul
metodei Split() si a unui set de caractere,utilizate ca separatori.
EXEMPLU:
using System;
public class SplitTest{
public static void Main() {
string words = "Lista de fructe:mere,pere,prune.";
string[] arie1 = words.Split(new Char[]{' ',',','.',':'});
foreach (string s in arie1) { Console.Write(s); }
Console.ReadLine();
}
}
Salvati fila cu numele Consola6.cs si compilati.
Se pot utiliza numeroase combinatii de caractere separator,pentru a
putea separa cuvintele din orice context,sau chiar si din tabele.
O alta aplicatie simpla o reprezinta cautarea unui cuvant,intr-un sir
de caractere,sau intr-o pagina de tip text.
EXEMPLU:
using System;
class Exemplu {
public static void Main() {
string s1 = "Paza buna trece primejdia rea.";
string s2 = "rea";
bool b;
b = s1.Contains(s2);
Console.Write("Sirul s1 contine sirul s2= {0}",b);
Console.ReadLine();
}
}
Salvati fila cu numele Consola7.cs si compilati.
Pentru a schimba formatul de reprezentare numerica,se poate utiliza
metoda Format().
EXEMPLU:
using System;
public class Exemplu2 {
public static void Main() {
long[] values={ Int16.MinValue,-2773,10,10423,Int16.MaxValue};
Console.WriteLine("{0,10] {1,10}\n","Decimal","hex");
foreach (short value in values) {
string formatat = String.Format("{0,10:G} : {0,10:X}",value);
Console.WriteLine(formatat);
}
Console.ReadLine();
}}
Salvati fila cu numele Consola8.cs si compilati.
-20-
De cele mai multe ori,datele de tip text sunt arhivate in file de tip
.txt sau .doc.Pentru a putea deschide o astfel de fila,se utilizeaza
obiectul File din System.IO.In continuare,fiecare linie din text poate
fi citita intr-un string si prelucrata dupa algoritmul dorit.
EXEMPLU:
using System;
using System.IO;
class Test {
public static void Main(){
string path = @"c:\text.txt";
string[] readText = File.ReadAllLines(path);
foreach (string s in readText)
{ Console.WriteLine(s); }
Console.ReadLine();
}
}
Salvati fila cu numele Consola9.cs si compilati.
In exemplul de mai sus,se preiau datele din fila test.txt (creata intr-un
exercitiu anterior).Incercati sa dezvoltati exercitiul in asa fel,incat
sa deschideti o fila text si apoi sa verificati existenta unui anumit
cuvant,introdus de la tastatura.
Pentru a formata textul,sunt admise urmatoarele secvente de control:
SECVENTA SEMNIFICATIE ECHIVALENT UNICODE
-----------------------------------------------------------------------
\0 Null 0x0000
\a Alert(beep) 0x0007
\b Backspace 0x0008
\t Horizontal tab 0x0009
\n New line 0x000A
\v Vertical tab(printing) 0x000B
\f Form feed (printing) 0x000C
\r Carriage return 0x000D
\" Double quote 0x0022
\' Single quote 0x0027
\\ Backslash 0x005C
\uABCD Unicode character 0xABCD
\xABCD idem
------------------------------------------------------------------------
Doua variabile,sau doua constante de tip string sunt echivalate a fi
egale,atunci cand au aceeasi lungime (numar de caractere),contin exact
aceleasi caractere,situate in exact aceeasi pozitie,sau daca ambele sunt
nule.
EXEMPLU: s1 = "Sirul de control";
s2 = "Sirul de control \n";
Cele doua siruri vor fi afisate identic,dar daca se face o operatie
de comparare,cele doua siruri nu vor fi egale.
Pentru a putea afisa orice alt tip de data compatibil,se poate apela
metoda ToString().
Pentru a forma expresii,variabiele si constantele din tipurile de mai
sus,sunt relationate prin operatori,la care se pot adauga unul sau mai
multe cuvinte cheie,cu rol de modificatori.
-21-
OPERATORI
-22-
Ori de cate ori este posibil,se vor prefera expresiile simple si clare,
usor de depanat.Atunci cand expresiile complexe sunt indispensabile,se
vor verifica toate valorile extreme ale expresiei.La nevoie se va include
expresia in una sau mai multe bucle de tip try...catch,pentru a putea
adauga si solutii de tratare a exceptiilor.Aceste bucle au rost mai ales,
pentru a evita operatiile imposibile,sau cu redundanta infinita,si pentru
a impiedeca blocarea executiei(Exemplu: procesorul nu poate gasi o anumita
valoare si cauta la infinit in memorie,sau asteapta introducerea acelei
date de la un periferic,sau din retea).
Tipurile de data,impreuna cu operatorii pot deja forma expresii
matematice simple.Pentru calcule matematice mai complexe,sau pentru a
putea utiliza si functiile trigonometrice,biblioteca System contine si
clasa Math,destinata special functiilor matematice.Printre membrii acestei
clase se numara si: Abs(),Asin(),Acos(),Ceiling(),Cos(),Sin(),Cosh(),Log()
Exp(),Floor(),Max(),Min(),Pow(),Round(),Sign(),Sqrt(),Tan(),Atan(),Tanh(),
Truncate() etc.
Apelul acestor functii se face direct,apeland membrii clasei Math().
EXEMPLU:
using System;
class Matematica {
public static void Main()
{
for (int i=90;i<100;i++){
double nr1 = Math.Sqrt(i);
Console.WriteLine("Radical din: {0:D} este= {1:N}",i,nr1);
nr1 = Math.Pow(i,2);
Console.WriteLine("Patratul lui: {0:D} este= {1:N}",i,nr1);
}
Console.ReadLine();
}
}
Salvati fila cu numele Consola10.cs si compilati.
Observati ca metoda Console.WriteLine() permite formatarea datelor de
output.( EXEMPLU: {0:D} si {1:N} ).Valorile acceptabile sunt:
C -format pentru valori monetare (bani)
D -format decimal
E -format exponential (stiintific)
F -format in virgula mobila
G -format general (E sau F in functie de necesitati)
N -format numeric
P -format procentual
X -format hexazecimal
Nu este necesara nici o operatie prealabila de conversie sau formatare.
Datele pot fi afisate direct in formatul dorit (vezi metoda).
Daca este necesar,codul poate fi insotit de comentarii explicative.
Comentariile lungi vor fi incluse intre: /* si */ iar cele care nu
sunt mai lungi de un singur rand,pot fi precedate de grupul "//".
EXEMPLU: // comentariu pe un singur rand
/* Comentariu ce se poate
extinde pe mai multe
randuri din program */
-23-
INSTRUCTIUNI
Denumite si directive de procesare,instructiunile au rostul de a seta
mediul de operare,sau de a organiza mediul de executie dupa o anumita
regula,astfel incat ordinea de executie a operatiilor sa asigure un flux
de date corect.Exista si instructiuni de preprocesare,semnalate tot cu
semnul # ca si in C++,cu rostul de a seta mediul de operare inainte de a
incepe procesarea codurilor din program (sunt la fel ca si in C++).
Unii autori,prefera termenul de instructiuni pentru controlul fluxului,
pentru a sublina faptul ca au rostul de a organiza ordinea de executie.
Importanta acestor instructiuni,depinde de stilul de programare.In cazul
programelor imperative,de tip "listing",rolul lor este esential,in timp
ce in cazul programarii structurate pe obiect,rolul lor este preluat mai
degraba de functii,metode si evenimente.Principalele instructiuni pentru
controlul fluxului sunt:
1. Liste si blocuri de instructiuni:
EXEMPLU: static void main(){ F();G();H();M(); }
2.Etichete si salturi "goto":
EXEMPLUL Functie(){ if(args.Length == 0)
goto sfarsit;
sfarsit:
Console.WriteLine("Sfarsit"); }
3.Declararea unor constante locale:
EXEMPLU: static void main(){ const float pi =3.141592 }
4.Declararea unor variabile locale:
EXEMPLU: static void main(){ int a = 2,b = 3,c= 5; }
5.Instructiuni complexe de tip expresie:
EXEMPLU: apelul unei functii static int F(int a,int b){}
static void Main(){ F(3,5) }
6.Instructiunea conditionala IF:
EXEMPLU: if (x >y) { Console.WriteLine("x este mai mare decat y");}
7.Instructiunea alternativa Switch Case:
EXEMPLU: static void Main(string[] args){ switch (args.Length) {
case 0:
Console.WriteLine("zero");
case 1:
Console.Writeline("unu");
}}
8.Bucla conditionala WHILE:
EXEMPLU: static void Main(string[] args) { int i = 0;
while (i < args.Length) { Console.WriteLine(args[i]);
i++; }}
9.Bucla conditionala DO...WHILE:
EXEMPLU: static void Main() { string s;
do { s = Console.ReadLine(); }
while (s != "Exit"); }
10.Bucla de repetitie FOR:
EXEMPLU: static void Main(string[] args){
for (int i = 0;i < args.Length;i++)
Console.WriteLine(args[i]); }
11.Bucla de iteratie FOREACH:
parcurge toate elementele dintr-o colectie
-24-
EXEMPLU: static void Main( string[] args) {
foreach (string s in args)
Console.WriteLine(s); }
12.Iesirea dintr-o bucla prin BREAK:
EXEMPLU: static void Main() { int x = 5;
while(x < 30) { Console.WriteLine("{0}",x);
x++;
if (x > 10)
break; }}
13.Continuarea unei bucle dupa instructiunea CONTINUE:
EXEMPLU: static void Main(string[] args) {
int i = 0;
while(true) { Console.WriteLine(args[i++]);
if (i < args.Length) continue;
break; }}
14.Specificarea datelor returnate,prin RETURN:
EXEMPLU: static int F(int a,int b) { return a + b ; }
static void Main(){ Console.WriteLine(F(2,3)); return; }
15.Evaluarea unui iterator prin YIELD:
EXEMPLU: static IEnumerable<int> FromTo(int a,int b) {
if ( a > b ) yield break;
for ( ; ; a++) { yield return a;
if (a == b) break; }}
16.Tratarea exceptiilor prin bucle TRY...CATCH:
EXEMPLU:
static int F(int a,int b){
if (b == 0 ) throw new Exception("Divide by zero");
return a / b; }
static void Main(){ try { Console.WriteLine(F(5,0)); }
catch(Exception e){Console.WriteLine("Error"); }}
17.Verificarea domeniului de valori prin operatorii CHECKED/UNCHECKED:
EXEMPLU: static void Main() { int x = Int32.MaxValue;
checked { Console.WriteLine(x+1); }
unchecked { Console.WriteLine(x+1); } }
18.Blocarea unui fir de executie (thread) prin LOCK:
EXEMPLU: lock (x) { DoSomething(); }
19.Alocarea de resurse prin instructiunea USING:
EXEMPLU: static void Main() { using (Resource r = new Resource()) }
-25-
CLASE SI OBIECTE
Programele mari,contin mult prea multe date,pentru a putea fi incluse
simultan in memoria procesorului.Pentru a putea procesa datele,este strict
necesar sa fie impartite in calupuri mai mici.Daca nu exista nici un fel
de structurare a programului,procesorul pur si simplu imparte datele in
functie de dimensiunea tamponului de memorie.In unele situatii insa,acest
mecanism poate duce la rezultate eronate sau aleatorii.Din acest motiv,
programatorii au inventat sisteme din ce in ce mai elaborate de organizare
a datelor,astfel incat fragmentarea programelor sa se faca doar in module
perfect functionale.Cea mai simpla solutie,o reprezinta impartirea codului
in mai multe file.Fiecare astfel de fila,va avea un nume propriu ( va fi
un spatiu denumit) si va putea fi incarcata separat in procesor.O alta
forma mai elaborata de structurare a datelor,este sub forma de containere
mai mult sau mai putin organizate,cum sunt:listele si enumerarile,tabelele
si bazele de date,ariile de date,structurile,clasele si obiectele.Un
astfel de container,nu numai ca are un nume propriu,dar contine si un set
oarecare de reguli fixe,pentru organizarea datelor.
Dintre aceste containere,clasele reprezinta forma cea mai complexa de
structurare a datelor.O clasa,este reprezentata prin setul de reguli fixe
ce urmeaza sa fie respectate in respectivul spatiu denumit.Clasa nu exista
propriu zis in program,ci este doar matrita pentru formarea unor entitati
reale,denumite obiecte.Un obiect este o instanta a unei clase si contine
atat indentificatorul spatiului denumit cat si datele propriu zise.In
procesor nu pot fi incarcate clase,ci doar obiectele create cu ajutorul
acestor clase.Pentru a tine evidenta lor,programul utilizeaza referinte
(pointeri spre adresa de memorie la care este arhivata definita lor).Se
poate spune despre clase ca sunt un tip de data,de tip referinta.
Clasele reprezinta un avantaj enorm pentru programator.Nu numai ca
izoleaza seturi de date si functii pentru procesarea lor,dar pot forma
module de program complet independente.Cu o singura referinta,procesorul
poate fi conectat la un intreg modul executabil.In momentul in care se
creaza un obiect,practic se creaza o referinta spre clasa sa de origine.
Toate aceste refernite sunt monitorizate de procesor,cu ajutorul unei
tabele de pointeri,astfel incat sa poata fi apelate,ori de cate ori este
necesar.In limbaj C#,intrega gestiune a memoriei se face automat.Nu mai
este necesar ca programatorul sa elibereze explicit memoria.Cu ajutorul
unui mecanism de tip "garbage collector" asemanator cu cel din Java (dar
cu implementare interna diferita),procesorul manipuleaza intern toate
obiectele si elibereaza pe cele inutile,dupa o secventa oarecare de
prioritati.Din acest motiv,este esential ca orice program C# sa fie
format din obiecte,sau componente.
Clasele pot mostenii definitii si declaratii de la alte clase,pot
impelemnta interfete si pot include date,sub forma de: constante,campuri
de date,variabile,metode si proprietati,evenimente si instructiuni.Dintre
metode,sunt esentiale constructorul si destructorul.Constructorul este o
functie mai speciala,ce se executa obligatoriu atunci cand se creeaza un
obiect,iar destructorul este o functie mai speciala ce se executa atunci
cand obiectul este eliberat din memorie.
Fiecare tip de data,poate fi precedat de urmatorii modificatori:
public,protected,internal,protected internal sau provate,pentru a preciza
domeniul de vizibilitate al datelor respective.
-26-
Cei cinci modificatori au urmatoarea semnificatie:
public -acces nelimitat
protected -acces limitat la clasa si clasele mostenitoare
internal -acces limitat la programul curent
internal protected -acces limitat in program si clasele mostenitare
private -acces limitat in tipul respectiv de data
Chiar daca sunt doar entitati virtuale,clasele trebuie sa fie definite
intr-o fila separata,denumita biblioteca cu alocare dinamica.Pentru a
crea un obiect,procesorul importa biblioteca DLL,citeste definitiile si
apoi creaza obiectul real.Deci,procesorul consuma memorie pentru a citi o
clasa.Acest fapt are importanta maxima,atunci cand creati astfel de file
de tip biblioteca DLL.Cu cat fila va fi mai mica,cu atat clasele continute
vor fi mai usor de importat.Este intotdeuna mai eficient sa creati mai
multe file mici,decat o singura biblioteca foarte mare.
Platforma Framework .Net contine un set extrem de bogat de astfel de
clase predefinite,arhivate in bibliotecile DLL standard.Pentru a crea un
obiect,este suficient sa fie importata biblioteca si sa fie apelat apoi
constructorul.
EXEMPLU:
using System;
using System.Windows.Forms;
namespace FereastraSimpla {
public class MyForm : System.Windows.Forms.Form {
public MyForm(){}
[STAThread]
static void Main(){ Application.Run(new MyForm()); }
}}
Salvati fila cu numele Clasa1.cs si compilati.
Se va produce un executabil ce deschide o fereastra Windows simpla.
Daca analizati putin codul,observati urmatoarele etape:
1. se importa bibliotecile sursa
2. se declara spatiul denumit (se fragmenteaza memoria)
3. se declara un obiect de tip Form (mostenit din clasa Windows.Forms)
4. se defineste constructorul clasei
5. se declara functia Main in care se apeleaza constructorul clasei
Expresia [STAThread] este facultativa si are rost pentru mecanismul de
"garbage collector" (seteaza prioritatea thread-ului).
Exemplul de mai sus,creaza un obiect standard,fara nici un fel de date
personalizate.Pentru a dezvolta obiectul,se pot adauga constante,campuri
de date,metode,proprietati si evenimente.Toate aceste date,pot fi complet
noi,caz in care trebuie sa contina atat declaratia cat si o definitie cat
mai amanuntita,sau pot fi creata cu ajutorul unor biblioteci standard,sau
al unor biblioteci DLL predefinite de d-voasta.
In majoritatea situatiilor moderne,se prefera obiectele vizuale
definite in biblioteca System.Windows.Forms.Pentru obiectele vizuale,se
prefera termenul de componente,preluat de la interfata de lucru vizuala,
in care crearea unui astfel de obiect se face cu un simplu click de mouse.
De fapt sunt obiecte obinuite,create cu ajutorul unor clase predefinite,
dar apelul constructorului se poate face si automat.Toate componentele
vizuale au un set foarte bogat de proprietati si metode standard,ce
rezolva toate necesitatile obisnuite de programare.
-27-
De exemplu,pentru a adauga un text in fereasta,se poate include si un
component de tip Label.Obiectul trebuie declarat,construit,setat si apoi
inclus in containerul de baza (in obiectul Form).
EXEMPLU:
using System;
using System.Windows.Forms;
namespace FereastraSimpla2 {
public class MyForm : System.Windows.Forms.Form {
private System.Windows.Forms.Label label1;
public MyForm(){ InitializeComponent(); }
private void InitializeComponent() {
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(101,48);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(46,13);
this.label1.TabIndex = 0;
this.label1.Text = "Textul dorit";
this.Controls.Add(this.label1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout();
}
[STAThread]
static void Main(){ Application.Run(new MyForm()); }
}}
Salvati fila cu numele Clasa2.cs si compilati.
Observati ca fata de exemplul precedent,au aparut si urmatoarele etape
suplimentare:
1. se declara un obiect de tip Label,numit label1
2. constructorul apeleaza o metoda speciala (InitializeComponent)
3. in functia de initializare se executa:
-se construieste obiectul label1 (apel constructor)
-se seteaza proprietatile
-se include label in in MyForm
-se seteaza proprietatile pentru MyForm
-se actualizeaza aspectul ferestrei
In acest exemplu am pastrat organizarea creata automat de platforma
Visual C# (practic am extras codul sursa),pentru a evidentia faptul ca
nu exista diferente esentiale intre compilatorul csc.exe si cel inclus
in platforma vizuala.Programarea cu componente vizuale se poate invata
si scriind coduri,linie cu linie.Acest gen de studiu va facilita foarte
mult orice operatie de depanare a codurilor.
In exemplul de mai sus,cuvantul cheie "this" este pointerul spre
instanta respectiva a obiectului.In lipsa sa,ar fi trebuit construit un
obiect explicit: object1 = new MyForm() si apoi fiecare component ar
fi trebuit sa fie referit prin: obiect1.label1 ...etc.
Constructorul va putea contine,una sau mai multe astfel de functii de
initializare,in functie de complexitatea interfetei grafice.
-28-
Orice variabila declarata in definitia unei clase,formeaza un membru
al clasei,denumit "camp de date" (field),pentru a se deosebi de orice
alta variabila simpla declarata la nivel de obiect.Si in interiorul
obiectelor,vor fi denumite tot "campuri de date",pentru a se diferentia
de variabilele simple(cele care exista doar in instanta respectiva).
Exista si un tip special de astfel de campuri de date,pentru care
se declara cate o metoda specializata Set() si Get(),astfel incat sa
poata accepta sau sa poata returna valoarea continuta.Acest tip special
de campuri de date,poarta numele de proprietati.
EXEMPLU: public class ButonulMeu {
private string caption;
public string Caption {
get { return caption; }
set { caption = value;
Repaint(); }
}}
In exemplul de mai sus,variabila de tip string "caption" este o
proprietate a clasei,deoarece poate fi setata cu set(),sau poate returna
valoarea continuta,cu get().Proprietatile sunt folosite extensiv,mai ales
pentru obiectele vizuale,deoarece permit utilizarea unor rutine automate
pentru setarea lor.Are rost sa definiti astfel de proprietati,mai ales
atunci cand oferiti si o intefata vizuala,prin care utilizatorul poate
interactiona cu obiectul cu un simplu click de mouse.
Interactiunea dintre utilizator si interfata grafica se face prin
intermediul unor mecanisme de control,denumite evenimente.Sistemul de
operare (Windows) emite cate un mesaj,pentru fiecare operatie de tip I/O
sau actiune executata de utilizator (vezi mesajele Windows).In Pascal,
aceste mesaje sunt asteptate si interceptate de functia GetMessage().In
Java,mesajele sunt interceptate si prelucrate de obiecte "listener",iar
in limbajul C#,au fost introduse niste clase speciale,denumite clase
"delegate".O astfel de clasa,contine si o referinta directa spre metoda
de tratare a evenimentului.Obiectul care emite mesajul,se numeste si
expeditor (event sender),iar cel care receptioneaza mesajul si apoi
prelucreaza solutia de raspuns,poarta numele de receptor (event reciver).
Biblioteca System,contine o clasa delegat,denumita EventHandler,destinata
anume pentru interceptarea si tratarea mesajelor Windows.
Pentru a programa un eveniment complet,sunt necesare trei etape:
1. -se declara un obiect de tip EventHandler ce contine o referinta
spre functia de tratare a evenimentului.
2. -se defineste functia de tratare a evenimentului
3. -se asociaza obiectul EventHandler la obiectul sender (cel care emite
mesajul Windows).
Nu se pot construi chiar orice fel de evenimente,decat daca definiti
si o functie specializata sa emita un eveniment oarecare.Componentele
vizuale (cele din System.Windows.Forms) au fiecare cate un grup de
mesaje standard,ce pot fi exploatate pentru a declansa evenimente.Daca
utilizati platforma Visual C#,acestea sunt prezentate sub forma de lista
in fereastra Properties / Events.Daca editati codurile manual,trebuie sa
studiati cu atentie bibliografia fiecarui obiect.
Cel mai simplu eveniment,dintr-o interfata vizuala este cel declansat
de apasarea unui buton.
-29-
EXEMPLU:
using System;
using System.Windows.Forms;
namespace ButonulMeu {
public class MyForm : System.Windows.Forms.Form{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button button1;
public MyForm(){ InitializeComponent(); }
private void InitializeComponent(){
this.label1 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
// se seteaza label1
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(101,48);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(46,13);
this.label1.TabIndex = 0;
this.label1.Text = "Eticheta";
// se seteaza button1
this.button1.Location = new System.Drawing.Point(104,94);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75,23);
this.button1.TabIndex = 1;
this.button1.Text = "Apasa butonul";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
// se seteaza Form1
this.AutoScaleDimensions = new System.Drawing.Size(6F,13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292,266);
this.Controls.Add(this.button1);
this.Controls.Add(this.label1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout();
}
private void button1_Click(object sender,EventArgs e)
{ label1.Text = "Butonul a fost apasat"; }
[STAThread]
static void Main(){ Application.Run(new MyForm()); }
}}
Salvati fila cu numele Clasa3.cs si compliati.
In exemplul de mai sus,se executa simultan doua operatii: se creaza un
obiect de tip System.Event.Handler (pentru evenimentul button1_Click) si
se asociaza acest obiect butonului button1,cu ajutorul operatorului +=
(se adauga metoda in lista de metode a obiectului button1).
Apoi se declara functia pentru tratarea evenimentului button1.Click().
Obiectul receptor si functia de tratare a evenimentului au acelasi nume,
pentru a putea fi identificate cat mai usor.
-30-
Un exemplu similar poate fi rezumat astfel:
EXEMPLU:
using System;
using System.Windows.Forms;
using System.Drawing;
-31-
doua mesaje similare,emise de obiecte diferite.In Pascal,toate mesajele
emise de sistem se aduna intr-o stiva,de unde sunt evaluate si prelucrate
in ordinea sosirii.Cu cat sunt mai multe evenimente,cu atat timpul de
asteptare este mai lung si apare riscul de a interfera cu mesaje emise de
un alt obiect.In mediul C#,acest tip de accident nu este posibil,deoarece
se specifica explicit,atat obiectul sender,cat si obiectul receptor.
Clasele de tip delegat,permit o flexibilitate de programare mult mai
mare,decat ascultatorii din Java.Astfel,un eveniment oarecare poate fi
conectat cu mai multe functii de tratarea evenimentului,ce pot fi
declansate simultan sau un cascada.Deasemenea,este posibil ca mai multe
evenimente sa fie conectate la aceeasi functie de raspuns.Exemplu: se va
obtine acelasi raspuns,la un click de mouse cu butonul drept,sau cu
butonul stang,sau prin apasarea tastei Enter.
In plus,clasa delegat nu intercepteaza decat strict mesajul dorit.Toate
celelalte mesaje sunt filtrate.Ca rezultat,nu exista o lista de asteptare
si nici riscul de a declansa metoda de raspuns pentru un mesaj eliberat
de un alt obiect.
Modul in care sunt alese si combinate evenimentele din interfata,face
obiectul unei profesiuni de sine statatoare (web designer) si nu poate
fi prezentat exhaustiv intr-un abecedar.In principiu,se vor alege doar
evenimentele cele mai clare,indispensabile pentru executia programului.
Facultativ,programatorul poate adauga si mici evenimente "secrete" cu
rostul de a facilita depanarea programului,sau de a oferii diverse
"artificii de programare",cu conditia ca aceste instrumente ajutatoare
sa nu interfereze cu executia normala a programului.
Pentru a creste,sau diminua numarul de mesaje ce pot fi emise de catre
obiectul sender,programatorul poate declara niste functii speciale,prin
care adauga sau elimina mesajele Windows din lista obiectului.
EXEMPLU: public class Panel1 {
private EventHandler handler;
public event EventHandler Click {
add { handler += value; }
remove { handler -= value; }
}}
De exemplu,daca doriti ca obiectul de tip Panel1 sa nu emita mesaje
la fiecare click de mouse,se va exclude mesajul WM_MOUSECLICK din lista
obiectului ( cu remove { handler -= WM_MOUSECLICK } ).
Se observa ca EventHandler functioneaza in obiectul sender la fel ca
o proprietate,dar in loc de Set() si Get(),accepta metodele Add() si
respectiv Remove().
Cu un astfel de mecanism,mesajele inutile nu numai ca nu vor mai fi
interpretate,dar nici macar nu vor mai fi emise,simplificand foarte mult
munca procesorului (se elibereaza memorie,pentru sute de pointeri sau
pentru alte operatii).
Nu este esential sa intelegeti mecanismul de lucru pentru evenimente,
decat daca proiectati programe profesionale.Platforma Visual C# ofera o
interfata extrem de facila,in care este suficient sa executati un click
pe evenimentul dorit si apoi sa completati codul pentru metoda de tratare
a evenimentului.Nu este bine sa atribuiti prea multe operatii unui singur
eveniment.Daca este esential,impartiti codul in mai multe functii de
raspuns,sau intre mai multe evenimente apropiate (butoane diferite).
-32-
Un tip special de metode,sunt cele ce pot fi utilizate in expresii,
pentru a executa diferite operatii.Acest gen de metode poarta si numele
de "operatori".Se pot defini astfel de clase,atunci cand lucrati frecvent
cu un anumit gen de operatii,pentru care nu exista un operator standard.
De exemplu,daca doriti sa transformati un numar analog,intr-un numar
digital,puteti scrie o clasa care executa conversia,salvati fila sub forma
de biblioteca DLL si apoi utilizati clasa pe post de operator.
EXEMPLU:
using System;
using System.Collections;
namespace NumarDigital {
public class Digital {
public static void Digit1(String Operand){
IEnumarator OperandEnum = Operand.GetEnumarator();
int CharCount = 0;
Console.WriteLine("Numarul in format digital este: ");
while (OperandEnum.MoveNext()){
CharCount++;
Console.Write(" '{0}' ",OperandEnum.Current);
}
Console.ReadLine();
}
}}
Salvati fila cu numele Digit1.cs.Pentru a crea fila DLL editati comanda:
csc /target:library /out:NumarDigital.DLL Digit1.cs
Apoi puteti exploata resursa cu o fila de genul:
using System;
using NumarDigital;
class telefon1 {
public static void Main(){
Console.WriteLine("Introduceti numarul de telefon (10 cifre): ");
string nr1 = Console.ReadLine();
Digital.Digit1(nr1);
}}
Salvati fila cu numele Digital1.cs.Pentru executabil editati comanda:
csc /out:Digital1.exe /reference:NumarDigital.DLL Digital1.cs
Exercitiul de mai sus este foarte simplist.Practic separa sirul in
caractere si afiseaza numarul sub forma de cifre izolate.Pentru a completa
programul,ar trebui ca numarul sa fie separat in digiti,in format numeric
de tip byte,ce vor fi arhivati intr-o arie.Apoi metoda returneaza aria,de
unde pot fi preluati si utilizati (de exemplu pentru a apela un numar de
telefon).Pentru exercitiu,puteti incerca sa realizati un astfel de
program complet,ce preia numarul sub forma de string si returneaza digiti
in format byte.
Cu acelasi tip de solutie,puteti sa definiti orice alt tip de operator
personalizat.Exemplu: executa automat calculul taxelor si impozitelor
pentru o anumita suma.
O singura clasa,poate contine mai multe astfel de metode,fiecare
dintre ele reprezentand un operator diferit.Acest mecanism,nu numai ca
va poate simplifica foarte mult viata,dar confera si un grad foarte mare
de siguranta (nu oricine poate apela biblioteca DLL personalizata).
-33-
Clasele C# permit supraincarcarea constructorilor cu conditia sa existe
o diferenta intre cele doua definitii: numarul de parametri,tipul lor,
modificatorii,domeniul de vizibilitate etc.Prin acest mecanism,este
posibil ca o clasa sa contina mai multi constructori diferiti,ce pot fi
apelati in functie de contextul programului.Fiecare astfel de constructor
va crea o instanta diferita a clasei respective (obiectele create vor fi
diferite,in functie de constructorul apelat).
EXEMPLU:
using System
class Point {
public double x,y;
public Point(){ this.x=0; this.y=0; }
public Point(double x double y){
this.x = x; this.y = y; }
}
class Test {
static void Main() {
Point a = new Point();
Console.WriteLine("a.x= {0}",a.x);
Point b = new Point(7,3);
Console.WriteLine("b.x= (0}",b.x);
Console.ReadLine();
}}
Salvati fila cu numele Constructori.cs si compilati.
Observati ca in clasa Point sunt definiti doi constructori distincti.
Primul nu are parametri,in timp ce cel de al doilea accepta doi parametri
de tip double.Daca se apeleaza primul constructor,variabila x va fi
initializata cu valoarea 0 (cea implicita),iar daca se apeleaza cel de
al doilea constructor,variabiala x va fi initializata cu valoarea din
apel (primul parametru).Acest mecanism este destul de frecvent si pentru
multe dintre clasele standardizate.Exista un constructor "default" in
care obiectul creat este initializat cu valori "default" si unul sau mai
multi constructori ce permit personalizarea obiectului in functie de
necesitatile de moment ale programatorului.
Destructorul se semanlizeaza cu o tilda:
EXEMPLU: using System;
class Point{ public double x,y;
public Point(double x double y){ this.x=x; this.y=y; }
~Point(){ Console.WriteLine("A fost apelat constructorul!");}
class Test{ static void Main() {
Point b = new Point(7,3);
Console.WriteLine("b.x= {0}",b.x);
Console.ReadLine();
}}
Salvati fila cu numele Destructor.cs si compilati.
Destructorul este apelat intotdeauna inainte de a elibera complet
obiectul din memorie,inclusiv de catre "garbage collector".Pentru a
putea observa apelul destructorului,lansati executabilul Destructor.exe
din Command Prompt cu comanda: Destructor.exe si apoi tastati Enter
ATENTIE: destructorul nu elibereaza memoria,ci doar executa un set
oarecare de operatii,inainte de eliberarea automata a memoriei.
-34-
Limbajul C# introduce si o inovatie: constructorul static,ce poate fi
apelat fara a construi o instanta a clasei respective.O astfel de clasa
se va utiliza in programe,pentru a putea initializa seturi intregi de
variabile,cu valori "default",fara sa se incarce memoria si cu un obiect
sursa.
EXEMPLU:
using System
class Text {
public static string s;
static Text() { s = "Text default"; }
}
class Test{
static void Main() {
Console.WriteLine(Text.s);
Console.ReadLine();
}}
Salvati fila cu numele Constructori2.cs si compilati.
Clasa Text contine un constructor static.Daca se va salva aceasta
clasa sub forma de biblioteca DLL,atunci membrul sau static s,va putea
fi apelat direct,fara a mai construi un obiect de tip s.
Daca intr-o clasa toate metodele si proprietatile sunt de tip static,
se poate crea o clasa intreaga de tip static.
EXEMPLU: static class StaticUtilities {
public static void HelperMethod() { ... }
}
Intr-o astfel de clasa,toti membrii pot fi apelati direct,fara sa mai
fie necesara crearea unui obiect din clasa respectiva.
EXEMPLU: StaticUtilities.HelperMethod(); // este un apel valid.
Clasele pot mosteni o alta clasa,la fel ca si in C++.
EXEMPLU:
using System;
class A {
public void F() { Console.WriteLine("Metoda F din clasa A");}
}
class B : A {
public void G() { Console.WriteLine("Metoda G din clasa B");}
}
class Test {
static void Main() {
B obiect1 = new B();
obiect1.F(); // mostenita din A
obiect1.G();
A obiect2 = obiect1; //primeste metoda F() din obiect1
obiect2.F();
Console.ReadLine();
}}
Salvati fila cu numele Mostenire.cs si compilati.
In exemplul de mai sus,observati ca obiect1 de tip B mosteneste clasa
A,in timp ce obiect2 de tip A nu primeste decat metoda F() din clasa A,
chiar daca o primeste prin intermediul obiectului obiect1.Daca se incearca
apelul obiect2.G() se returneaza o eroare (metoda nedefinita).
-35-
Exista si situatii cand o clasa poate fi definita in mai multe file
sursa separate.Acest gen de situatii poarta numele de clase partiale si
se intanesc fie atunci cand definitia clasei este prea stufoasa,fie cand
o clasa complexa este creata automat,cu date preluate de la mai multi
clienti din retea.In toate aceste situatii,se va utiliza cuvantul cheie
"partial" pentru a semnala faptul ca exista si definitii declarate in alta
fila sursa.
EXEMPLU: // Fila1.cs contine:
partial class ClasaMea {
private int[] counts;
public string ToString() { ....} }
// Fila2.cs contine:
partial class ClasaMea{
private int value;
private void Helper() { ......} }
Cand se vor compila cele doua file,se va construi o singura clasa in care
sunt incluse toate definitiile din cele doua file surse.Pentru un exemplu
complet,vezi constructia bibliotecii DLL "FunctiileMele" din capitolul
Compilator.Acest mecansim este valabil pentru orice tip de data,nu doar
pentru clase.Principalul avanataj al acestui mecanism se poate observa
atunci cand se actualizeaza o biblioteca DLL,sau o fila sursa.In loc sa
fie rescrise toate filele sursa,se adauga doar fila auxiliara ce contine
codurile pentru actualizare.
STRUCTURI
-36-
EXEMPLU:
using System;
using System.Collections;
class Point {
public int x,y;
public Point(int x,int y) {
this.x = x;
this.y = y; }}
class Test {
static void Main() {
long mem1,mem2,mem3;
mem1 = System.GC.GetTotalMemory(false);
Point[] points = new Point[10000];
for (int i=0;i<10000;i++)
points[i] = new Point(i,i*i);
mem2 = System.GC.GetTotalMemory(false);
mem3 = mem2-mem1;
Console.WriteLine("Memoria initiala: {0:F}",mem1);
Console.WriteLine("Memoria dupa alocare: {0:F}",mem2);
Console.WriteLine("Diferenta = {0:F}",mem3);
Console.ReadLine();
}}
Salvati fila cu numele Struct1.cs si compilati.La executie puteti
observa ca pentru a gestiona cele 10000 de puncte de tip Point,au fost
create 10000 de obiecte,cu un consum de memorie de 204256 bytes.Copiati
exemplul si inlocuiti tipul class prin struct:
using System;
using System.Collections;
struct Point {
public int x,y;
public Point(int x,int y) {
this.x = x;
this.y = y; }}
class Test {
static void Main() {
long mem1,mem2,mem3;
mem1 = System.GC.GetTotalMemory(false);
Point[] points = new Point[10000];
for (int i=0;i<10000;i++)
points[i] = new Point(i,i*i);
mem2 = System.GC.GetTotalMemory(false);
mem3 = mem2-mem1;
Console.WriteLine("Memoria initiala: {0:F}",mem1);
Console.WriteLine("Memoria dupa alocare: {0:F}",mem2);
Console.WriteLine("Diferenta = {0:F}",mem3);
Console.ReadLine();
}}
Salvati fila cu numele Struct2.cs si compilati.La executie,puteti
observa ca in acest caz,s-au consumat doar 80024 de bytes,pentru cele
10000 de puncte de tip Point,deoarece s-a construit un singur obiect
de tip arie,cu 10000 de elemente.
-37-
Exemplul de mai sus,este un exemplu tipic de solutie pentru optimizarea
codului.Nu numai ca s-au inlocuit 10000 de obiecte prin unul singur,dar
s-a renuntat si la cei 10000 de pointeri spre fiecare obiect.
Din acest motiv,tipul struct este utilizat extensiv si in bibliotecile
standard,alaturi de clase.De exemplu,biblioteca System contine si o astfel
de structura dedicata pentru operatiile cu date calendaristice,denumita
DateTime.Membrii sai,pot fi apelati la fel ca si cei ai claselor standard.
EXEMPLU:
using System;
class DataNasterii {
static void Main(){
int an,luna,zi;
Console.WriteLine("Introduceti anul nasterii: ");
an = System.Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Introduceti luna in cifre -Ex. 5 pentru luna Mai:");
luna = System.Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Introduceti ziua nasterii: ");
zi = System.Convert.ToInt32(Console.ReadLine());
DateTime datanasterii = new dateTime(an,luna,zi);
DateTime acum = DateTime.Now;
long impulsuri = acum.Ticks - datanasterii.Ticks;
TimeSpan durata = new TimeSpan(impulsuri);
Console.WriteLine("De la data nasterii,au trecut pana azi {0:f}:",acum);
Console.WriteLine(" {0:N0} impulsuri",impulsuri);
Console.WriteLine(" {0:N0} secunde ",durata.TotalSeconds);
Console.WriteLine(" {0:N0} minute",durata.TotalMinutes);
Console.WriteLine(" {0:N0} zile,{1} ore,{2} minute,{3} secunde",
durata.Days,durata.Hours,durata.Minutes,durata.Seconds);
Console.ReadLine();
}}
Salvati fila cu numele Struct3.cs si compilati.
In mod similar,puteti scrie o aplicatie ce masoara intervalul de timp
dintre doua evenimente,sau dintre doua date calendaristice.
Daca analizati putin membrii structurii DateTime,puteti observa ca
exista 11 constructori pentru tipul DateTime cu parametrii de tip int32
sau int64,ce permit formule foarte sofisticate de formatare a datelor de
tip data calendaristica.Cu maximum de economie de memorie,se pot face
operatii extrem de complexe.
Ori de cate ori oscilati intre tipul class si tipul struct,este bine
sa evaluati necesitatile viitoare si solutiile de procesare scontate,dar
sa faceti si o analiza sumara a consumului de memorie.Cu cat se va
utiliza mai putina memorie,si cu cat memoria consumata va fi eliberata
mai rapid,cu atat aplicatia va fi mai performata si va fi executata mai
usor.Pentru analiza memoriei,cel mai simplu este sa apelati la membrii
clasei GC (garbage collector) din System (vezi si Struct1.cs).Evaluati
memoria consumata in diferite puncte de executie ale programului,si apoi
alegeti solutia cea mai buna.In cazul programelor mari,este bine sa
programati si o caseta de control,in care se afiseaza in permanenta
consumul de memorie.Pastrati aceasta caseta,pana cand terminati,apoi
optimizati programul.In final,dupa epuizarea etapei de depanare si control
caseta poate fi eliminata,pentru a nu deruta utilizatorul.
-38-
INTERFETE
Interfetele sunt un contract intre programator si aplicatie.O astfel
de interfata,impune implementarea in program a tuturor datelor declarate
in interfata.Sunt un fel de clase abstracte,create special pentru a fi
mostenite.O interfata nu poate fi utilizata pentru a crea obiecte sau
functionalitati,ci doar pentru a forma un fel de sablon al aplicatiei.
Sunt utilizate mai ales in mediul vizual,unde poata numele de interfete
grafice.Au rostul de a organiza componentele unui program,intr-o maniera
fixa,cu care utilizatorul a fost deja familiarizat anterior.
EXEMPLU: In interfata se declara o fereastra si un meniu.
Toate programele in care se utilizeaza aceasta interfata vor
utiliza pentru prezentarea datelor o fereastra cu meniul respectiv.
Pentru a declara o interfata,se utilizeaza cuvantul cheie "interface",
in loc de "class".Din punct de vedere tehnic,o interfata este un fel de
clasa abstracta,ce nu poate fi utilizata pentru a construi obiecte.Intr-o
interfata,se includ doar declaratii,fara nici o solutie de implementare.
O interfata poate contine metode,proprietati si evenimente,dar nu poate
contine constructori,destructori sau operatori supraincarcati.Interfata
este prin definitie creata pentru a fi mostenita,asa ca toate datele
incluse in interfata sunt publice.Nu se pot utiliza nici un fel de
modificatori (gen virtual,static etc.).
O clasa care implementeaza o interfata,trebuie sa asigure si declaratia
completa a tuturor datelor importate din interfata.
EXEMPLU:
using System;
interface Conversie { void SetText(); }
class Test : Conversie {
static void Main() {
Test t1 = new Test();
t1.SetText();
Console.ReadLine(); }
public void SetText(){
Console.WriteLine("Se executa metoda contractata !");}
}
Salvati fila cu numele Interfata1.cs si compilati.
Se observa urmatoarele etape:
1.-se declara interfata Conversie ce contine metoda SetText()
2.-se creaza clasa Test ce mosteneste interfata Conversie
3.-se declara explicit metoda contractata (metoda SetText())
4.-se construieste un obiect din clasa Test
5.-se apeleaza metoda SetText()
In exempul de mai sus,interfata contine o singura metoda ce returneaza
un text default.Se observa cu usurinta ca o astfel de functionalitate se
poate exploata pentru a forta presetarea unor anumite valori (setarea
mediului de executie),inainte de a incepe executia propriu zisa a unui
program.De cele mai multe ori,aceasta presetare implica construirea unui
set de obiecte vizuale,prin care utilizatorul va putea comunica cu
aplicatia,cu ajutorul unor clik-uri de mouse.Acest gen de interfata,poarta
numele de "interfata grafica cu utilizatorul".Cea mai cunoscuta este
interfata API Windows,in care utilizatorul va beneficia doar de obiecte
vizuale de tip Windows.
-39-
Interfetele permit un mecanism de mostenire multipla.O singura clasa,
poate mosteni simultan mai multe interfete.
EXEMPLU: class ClasaMea: Interfata1,Interfata2,Interfata3{...}
Deasemenea,o interfata poate mosteni o alta interfata.
EXEMPLU: interface Interfata2: Interfata1 { ...}
Atunci cand se utilizeaza un astfel de mecanism de mostenire multipla,
depanarea unei metode mostenite multiplu poate deveni uneori destul de
confuza.Pentru a evita supraincarcarea unui metode mostenite din mai
multe interfete succesiv,este posibil ca la nivel de clasa de implementare
sa se faca o declaratie explicita a metodei respective.In acest caz,
metoda respectiva nu va mai fi declarata public,ci va fi declarata cu
ajutorul unui nume calificat (spatiu denumit).O astfel de metoda nu va
mai putea fi apelata ca simplu membru al clasei respective ci va trebui
construit initial un obiect,apoi se va apela metoda obiectului.Prin
acest mecanism,se garanateaza ca metoda este cea implementata explicit.
EXEMPLU:
using System;
interface Conversie { void SetText();}
class Test: Conversie {
void Conversie.SetText{
Console.WriteLine("Se executa metoda contractata !");
}}
class Executie {
static void Main(){
Test t1 = new Test();
Conversie t2 = t1;
t2.SetText();
Console.ReadLine();
}}
Salvati fila cu numele Interfata2.cs si compilati.
In exemplul de mai sus,daca se face apelul t1.SetText() se va returna
un mesaj de eroare,deoarece metoda Test.SetText() nu este statica sau
publica.Presupundand ca in exemplul de mai sus ar fi existat mai multe
metode SetText(),mostenite din interfete diferite,acest mecanism asigura
apelul metodei SetText() definita in clasa Test,spre deosebire de orice
alta metoda SetText() implementata altfel.
Bibliotecile standard contin numeroase astfel de interfete,ce pot
fi utilizate pentru a implementa functionalitati standard.De exemplu,
billioteca System contine si intefetele: IComparable,IConvertible,
IDisposable etc.
EXEMPLU: daca o clasa oarecare mosteneste interfata IDisposable din
System,atunci este sigur ca exista si o metoda denumita Dispose() pentru
operatii de eliberare a memoriei (deoarece interfata IDisposable contine
ca membru unic metoda Dispose()).
Interfetele permit crearea de solutii standard,ce pot fi utilizate si
de alti programatori.Apeland utilitarul ildasm.exe,un programator va putea
observa ce intefete sunt implementate si ce functionalitati pot fi apelate
in modulul importat.
EXEMPLU: deschideti modulul Interfata1.exe cu ildasm.exe pentru a
putea observa: intefata Conversie si clasa de implementare Test.
Codul metodei SetText va putea fi studiat in clasa de implementare (Test).
-40-
CLASELE DE TIP DELEGATES
Exista numeroase situatii de programare in care este utila inlocuirea
apelului unei functii,cu un pointer.Exemplu: atunci cand o functie este
utilizata pentru a furniza un parametru unei alte functii.In alte situatii
este necesar ca o anumita functie sau metoda sa fie executata atunci cand
se indeplineste o anumita conditie.Pentru acest scop,este necesar ca
metoda respectiva sa fie conectata la evenimentul respectiv,printr-o
structura oarecare de date.Limbajul C# a rezolvat acest gen de situatii,cu
ajutorul claselor de tip Delegate.Clasa Delegate,definita in System si
varianta sa mai perfectionata MulticastDelegate sunt niste clase speciale
ce nu pot fi utilizate pentru a crea obiecte.Declararea acestor clase se
face prin cuvantul cheie "delegate",in loc de "class",tocmai pentru a se
evita orice fel de confuzii.
Daca se declara o astfel de clasa,compilatorul va crea automat o clasa
delegat (la care programatorul nu are acces).Aceasta clasa va avea niste
metode intrinseci (BeginInvoke,Invoke si EndInvoke) prin care asigura
functionalitatea automata (nu pot fi apelate din program).Clasa astfel
creata va contine si o conexiune spre metoda dorita,astfel ca prin apelul
delegatului sa poata fi pusa in executie metoda dorita.Pentru a realiza
o astfel de conexiune sunt necesare urmatoarele etape:
1. se declara delegatul si tipul parametrilor din functia conectata
2. se declara metoda ce urmeaza sa fie conectata la obiect
3. se creaza un obiect de tip delegat
4. se utilizeaza delegatul in locul metodei
EXEMPLU:
using System;
namespace Program1{
delegate void DelegatulMeu();
class Test{
static void Main(){
DelegatulMeu d1 = new DelegatulMeu(FunctiaMea);
d1();
Console.ReadLine();
}
static void FunctiaMea() {
Console.WriteLine("Se executa metoda conectata !"); }
}
}
Salvati exemplul cu numele Delegat1.cs si compilati.
Asadar se declara o clasa de tip delegate,denumita DelegatulMeu,fara
nici un parametru.Ca urmare,acest delegat va putea fi conectat la orice
functie fara nici un parametru.Apoi se declara functia dorita si se
face conexiunea construind un obiect de tip delegat (spre functia dorita).
Acesta este cel mai simplu exemplu posibil,cu o singura functie,fara
nici un parametru.Practic,aproape ca nu are rost crearea unui delegat,
decat pentru a simplifica apelul.
Exista insa si situatii in care se utilizeaza mai multe functii,cu
mai multi parametri,dar o singura clasa delegat.In acest caz,se va
crea automat o clasa de tip MulticlastDelegate (definita tot in System).
Singura conditie obligatorie este ca toate metodele conectate sa contina
aceiasi parametri ca si delegatul.
-41-
EXEMPLU:
using System;
namespace DelegatMultiplu {
public delegate int Delegat1(int x,int y);
public class Mate{
public static int Adun(int x,int y){ return x+y; }
public static int Mul(int x,int y){ return x*y;}
public static int Divide(int x,int y){ return x/y;}
}
public class Executie {
public static void Main(){
Delegat1 a1 = new Delegat1(Mate.Adun);
Delegat1 m1 = new Delegat1(Mate.Mul);
Delegat1 d1 = new Delegat1(Mate.Divide);
Console.WriteLine("Delegatul pentru adunare returneaza:");
Console.WriteLine(a1(32,4));
Console.WriteLine("Delegatul pentru inmultire returneaza:");
Console.WriteLine(m1(32,4));
Console.WriteLine("Delegatul pentru impartire returneaza:");
Console.WriteLine(d1(32,4));
Console.ReadLine();
}}}
Salvati fila cu numele Delegat2.cs si compilati.
Observati ca in acest caz,clasa de tip MulticastDelegate va putea
conecta orice functie cu doi parametri de tip int.Conexiunea cu functia
nu se face decat in momentul in care se creaza obiectul de tip delegat.
Astfel,cu o singura clasa se pot crea delegati spre mai multe functii
diferite.Eventual se poate construi chiar o arie de delegati ce pot fi
apelati serial,cu rutine automate.Acest gen de solutie,simplifica foarte
mult situatiile in care un numar oarecare de functii si metode trebuie
sa fie apelate repetat,dupa un algoritm oarecare.
Daca deschideti modulul Delegat2.exe cu utilitarul ildasm.exe puteti
observa ca namespace DelegatMultiplu contine trei clase: Delegat1,Executie
si Mate.Dintre acestea,clasa Delegat1 este clasa de tip MulticastDelegate
creata automat in momentul compilarii.Daca deschideti aceasta clasa puteti
observa metodele intrinseci.
Cu o singura astfel de clasa,se pot controla zeci sau sute de functii
si metode diferite (inlocuind zeci sau sute de clase si pointeri).Se
observa robustetea cu care este gestionata memoria.Din acest motiv,clasele
de tip "delegate" sunt utilizate in limbajul C# pentru a gestiona toate
evenimentele din program.In locul functiilor pentru captarea,filtrarea
si tratarea mesajelor Windows,se utilizeaza delegati.
Biblioteca System include un set destul de bogat de delegati standard
ce pot fi utilizati pentru problemele curente de programare.Dintre acestia
cei mai importanti sunt: Action,Action<T>,Action<T1,T2>...Action<T1...T15>
AppDomainInitializer,AsyncCallback,Comparison<T>,Converter<TInput,TOutput>
EventHandler,EventHandler<TEventArgs>,Func<TResult>,Func<T1...T15>,
Predicate<T>,ResolveEventhandler si UnhandledExceptionEventhandler.
Pentru detalii si exemple de implementare,este recomandabil sa studiati
sursele bibliografice pentru .Net si 3.5 Framework Class Library sau
manualele de tip tutorial din retea.
-42-
Cea mai frecventa aplicatie a claselor delegat o reprezinta gestionarea
evenimentelor Windows.Pentru acest scop,se utilizeaza clasele delegate:
EventHandler si EventHandler<TEventArgs>.In cazul evenimentelor,clasa
de tip delegate conecteaza obiectul sender(cel care emite mesajul Windows)
cu functia pentru tratarea evenimentului.Majoritatea functiilor de tratare
a evenimentului asteapta un singur mesaj Windows,adica au un singur
parametru.Ca urmare,cu o singura clasa de tip delegate se pot gestiona
toate evenimentele unei intefete grafice.
EXEMPLU:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Eveniment1 {
public class MyForm: System.Windows.Forms.Form {
TextBox t1 = new TextBox();
Button b1 = new Button();
public MyForm(){
t1.Location = new Point(20,30);
b1.Text = "Apasa aici !";
b1.Location = new Point(20,55);
b1.Size = new Size(150,20);
b1.Click += new EventHandler(OnClick);
this.Controls.Add(t1);
this.Controls.Add(b1);
this.Resize += new EventHandler(OnResize);
}
public void OnResize(object sender,EventArgs ee) {
MessageBox.Show("oops! Redimensionare !"); }
public void OnClick(object sender,EventArgs e) {
t1.Text = "Hello user !"; }
[STAThread]
static void Main() { Application.Run(new MyForm());}
}}
Salvati fila cu numele Ev1.cs si compilati.
Observati ca prin operatorul += se executa de fapt doua operatii
simultane,se creeaza un nou obiect de tip EventHandler (de tip delegate)
si se conecteaza obiectul ca metoda de tratare a evenimentului.
Prin acest mecanism,toate evenimentele din interfata grafica pot fi
controlate cu ajutorul obiectelor de tip EventHandler ce inlocuiesc
tabela de pointeri creata in mediul Windows.
Daca deschideti fila Ev1.exe cu utilitarul ildasm.exe,puteti observa
ca nu se creaza o clasa delegat noua ci se utilizeaza System.EventHandler.
Obiectele EventHandler se creaza in momentul executiei si exista doar in
memoria de operare.Fila Ev1.exe nu contine decat clasa Eveniment1.MyForm.
Cele doua obiecte pot fi regasite in metoda constructor (.ctor:void()).
Prin acest mecanism robust si simplu,C# nu numai ca protejeaza aceste
obiecte de la suprascrierea lor accidentala,dar si executa operatiile
esentiale la adapost de privirile indiscrete ale "hacker-ilor".
Evenimentele si clasele delegat,reprezinta un punct nodal in executia
programelor C#.Din acest motiv,este recomandabil sa studiati in amanunt
toata documentatia referitoare la aceste subiecte.
-43-
INDEXATORI
-44-
GENERICE
-45-
In mod curent,genericele deschise (open generic) se utilizeaza atunci
cand se declara clasa,iar genericele inchise (closed generic) atunci cand
se creaza instanta (obiectul).
In exemplul precedent,clasa Container defineste o metoda unica,fara
a face distinctia intre un tip de data sau altul.In mod curent insa,se
pot utiliza formule de filtrare,prin care rezultatul functiei este
diferit,in functie de tipul de data al instantei.
EXEMPLU:
using System;
-46-
Dintre clasele tipizate,cea mai comuna este clasa List<T>.Aceasta
clasa este un container ce poate organiza liste de obiecte.Intr-un astfel
de container,obiectele din lista pot fi apelate prin indexul lor.List<T>
este un obiect echivalent cu o arie de date,la care se adauga metodele
standard: Contains,IndexOf,LastIndexOf,Remove,BinarySearch sau Sort.
EXEMPLU:
using System;
using System.Collections.Generic;
nume.Add("Mircea");
nume.Add("Stefan");
nume.Add("Tudor");
nume.Add("Isabela");
nume.Add("Ana");
Console.WriteLine();
foreach(string persoana in nume)
{ Console.WriteLine(persoana); }
Console.WriteLine("\n Capacitate: {0}",nume.Capacity);
Console.WriteLine("Inregistrari: {0}",nume.Count);
Console.WriteLine("\n Contine elementul:(\"Isabela\")? ={0} ",
nume.Contains("Isabela"));
List<int> numere = new List<int>();
numere.Add(3);
numere.Add(27);
numere.Add(38);
foreach(int numar in numere)
{ Console.WriteLine("Elementul este: {0}",numar); }
Console.ReadLine();
}}
Salvati fila cu numele Generic3.cs si compilati.
In exemplul de mai sus,clasa List<T> a fost apelata pentru a crea doua
obiecte de tip lista.Primul denumit "nume" ce contine obiecte de tip
string si al doilea,denumit "numere" ce contine obiecte de tip Int.
Se observa ca obiectele din lista pot fi citite secvential cu o bucla
foreach (sau cu o bucla definita de programator).
In plus,metodele obiectului permit operatii simple de genul:
-cate inregistrati contine lista
-cate inregistrari s-au adaugat la ultima operatie
-ce capacutate are obiectul
-eliminarea unor obiecte din lista
-etc.
Lista generica este un obiect robust,usor de utilizat,ce inlocuieste
cu succes orice structura,enumerare sau container cu indexatori.Este
recomandabil sa utilizati acest tip de obiect,mai ales in modulele pe
care doriti sa le partajati si cu alti programatori.
-47-
O alta clasa generica similara cu List<T>,dar mai perfectionata este
clasa Dictionary<TKey,TValue>.Aceasta clasa contine doi parametri generici
si permite astfel un numar foarte mare de implementari posibile.Poate
asigura toate operatiile executate de List<T>,la care se adauga si un
set intreg de operatii de indexare in functie de TKey.Fiecare obiect de
tip TValue este indexat la un obiect de tip TKey,pentru a forma perechi
de obiecte,ce permit numerosi algoritmi pentru sortarea sau filtrarea
lor.
EXEMPLU:
using System;
using System.Collections.Generic;
-48-
Clasele generice deschise accepta orice tip de data definit in program.
Exista insa si situatii in care doriti sa impuneti un anumit tip de
restrictii,sau de limitari,astfel incat clasa generica sa nu poata accepta
decat o anumita subcategorie de tipuri de data,sau chiar doar un anumit
tip de data.Aceste constrangeri (constrains) se pot obtine adaugand
cuvantul cheie "where" urmat de tipul de constrangere.In cazul in care
clientul incearca sa creeze o instanta cu un alt tip de data,se va returna
o eroare de compilare.Sunt posibile urmatoarele constrangeri:
1. where T: struct -argumentul trebuie sa fie de tip valoric
2. where T: class -argumentul trebuie sa fie o referinta spre o clasa
o interfata,un delegat sau o arie
3. where T: new() -argumentul trebuie sa detina un constructor fara
nici un parametru
4. where T: <base class name> -argumentul trebuie sa fie clasa de baza
sau o clasa derivata din clasa de baza
5. where T: <interface name> -argumentul trebuie sa fie,sau sa
implementeze si interfata specificata
6. where T: U -argumentul trebuie sa fie argumentul genericului
U sau sa fie derivat din acesta
EXEMPLU:
using System;
public class s1 {
public s1(){ Console.WriteLine()("Textul dorit"); }
}
public class Container<T> where T: s1 {
public void Scrie(T input){
Console.WriteLine("Este textul preluat din fisier!");
}}
class ProgramulMeu{
static void Main(){
Container <s1> text1 = new Container <s1>();
text1.Scrie(new s1());
Console.ReadLine();
}}
Salvati fila cu numele Generic5.cs si compilati.
In exemplul de mai sus,clasa generica Container nu poate fi apelata
decat pentru obiecte de tip s1.Presupunand ca exista o arhiva de memorie
formata din obiecte de tip s1 si clientul lucreaza cu o interfata grafica
in care se solicita datele dorite,acesta va trebui sa introduca in caseta
de solicitare numele clasei s1.Orice alt tip de data va returna o eroare.
Clienti diferiti,vor avea acces la fisiere diferite,in functie de codurile
de acces (constrangerile claselor generice).Cu alte cuvinte,acest tip de
solutie,permite impartirea clientilor in grupe cu nivelele diferite de
acces la informatie,fara a fi necesara introducerea de parole,coduri de
acces,sisteme de securizare...etc.
Clasele generice reprezinta forma cea mai evoluata de structurare a
datelor,in cadrul conceptului general de programare orientata spre obiect.
Atentie insa: genericele deschid o poarta larga spre nenumarate erori de
executie,exceptii de format,cazuri particulare,incompatibilitati,bucle
infinite,tipuri nedefinite,erori de sintaxa,conversii gresite...etc.
-49-
ENUMERARI
-50-
EXEMPLU:
using System;
public class Enumerare {
enum Culori { rosu,galben,albastru,verde,alb };
static void Main(){
Culori col = Culori.rosu | Culori.verde;
switch(col) {
case Culori.verde:
Console.WriteLine("Culoarea verde este in lista!");
break;
}
Console.ReadLine();
}}
Salvati fila cu numele Enumerare2.cs si compilati.
In exemplul de mai sus,bucla switch...case nu numai ca verifica daca
elementul cautat face parte din lista,dar permite si executia unei rutine
de raspuns la apel.Intr-un program mai mare,utilizatorii vor avea cate un
set de valori prestabilite,in functie de optiunile pentru care au optat.
In momentul executiei,fiecare utilizator va putea avea o configuratie
proprie,in functie de unul sau mai multe astfel de seturi de valori
prestabilite.
Daca elementele din enumerare sunt de alt tip decat Int32,acest tip
trebuie specificat explicit,atat atunci cand se declara enumerarea cat si
atunci cand se citeste un element al sau.
EXEMPLU:
using System;
public class Enumerare {
enum Domeniu : long { Max = 123456789L,Min = 4321L };
static void Main() {
long x = (long)Domeniu.Max;
long y = (long)Domeniu.Min;
Console.WriteLine("Valoarea maxima este = {0},x);
Console.WriteLine("Valoarea minima = {0}",y);
Console.ReadLine();
}}
Salvati exemplul cu numele Enumerare3.cs si compilati.
-51-
ITERATORI
-52-
Nu se justifica construirea unui iterator,doar pentru a parcurge toate
elementele dintr-o colectie.Acelasi rezultat se poate obtine cu o bucla
simpla,FOREACH sau FOR...NEXT.In mod normal,iteratorul contine o functie
specializata,prin care se executa o serie oarecare de operatii asupra
datelor returnate,fara a modifica datele din resursa exploatata.Fie ca
este vorba de operatii aritmetice sau de formatare,fie ca se executa
selectii,sortari sau filtrari,iteratorul trebuie sa automatizeze un set
oarecare de operatii.
EXEMPLU:
using System;
using System.Collections;
class ListClass : System.Collections.IEnumerable {
string[] nume = { "Ion","Dan","Stefan","Constantin"};
public System.Collections.IEnumerator GetEnumerator()
{ for (int i=0; i< nume.Length; i++) { yield return nume[i]+"escu,";}
}}
class ProgramulMeu{
static void main(){
ListClass listClass1 = new ListClass();
Console.WriteLine("Numele dun lista sunt: ");
Console.WriteLine("");
foreach (string i in listClass1)
{ System.Console.Write(i); }
Console.ReadLine();
}}
Salvati fila cu numele Iterator2.cs si compilati.In exemplul de mai
sus,iteratorul nu numai ca parcurge intreaga colectie,dar executa si o
operatie oarecare (adauga sufixul "escu").In mod similar,se pot executa
serii de calcule aritmetice (Exemplu: calculul impozitelor etc.).Pentru
fiecare set de operatii,se poate defini un astfel de iterator personalizat
ce poate fi arhivat in memorie sub forma de biblioteca DLL.Cu o astfel
de solutie,se pot valorifica datele dintr-o resursa,dupa algoritmi
personalizati.Spre deosebire de un tabel sau o baza de date,o astfel de
solutie prezinta o siguranta crescuta in exploatare,deoarece datele nu
pot fi valorificate decat in prezenta iteratorului.
Mai mult decat atat,interfata IEnumerable<T> expune un enumerator de
tip generic si un set complet de metode,ce permit operatii similare cu
sistemul de interogari SQL utilizat in cazul bazelor de date.Dintre
metodele acestei interfete,merita amintite:Aggregate<TSource>,All<TSource>
Any<TSource>,Average<TSource>,Contains<TSource>,Count<TSource>,Distinct<
TSource>,Elements<TSource>,First<TSource>,GroupBy<TSource>,GroupJoin<
TSource>,Join<TSource>,Last<TSorce>,Max<TSource>,Min<TSource>,OrderBy<
TSource>,Select<TSource>,Single<TSource>,Sum<TSource>,ToList<TSource>,
Union<TSource>,Where<TSource>,Zip<TSource> (vezi referinta MSDN).
Exista mai multe metode supraincarcate,pentru fiecare dintre operatiile
de mai sus,astfel ca aceasta interfata se poate adapta practic la orice
necesitati de programare curenta.Interfata poate fi apelata direct,caz
in care nu este necesar sa redefiniti sau sa supraincarcati nici una
dintre metode,ci puteti apela direct metoda standard.Cambinand iteratorii
cu selectii de tip LINQ (vezi si capitolul urmator),se obtine o foarte
mare flexibilitate de filtrare a datelor de orice tip.
-53-
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class ProgramulMeu{
class Angajat
{ public string Nume { get; set; }
public int Vechime { get; set; }
}
static void Main() {
Angajat[] an1 = {
new Angajat { Nume="Ion Ionescu",Vechime=18 },
new Angajat { Nume="Mihai Popescu",Vechime=14},
new Angajat { Nume="Stefan Georgescu",Vechime=3 },
new Angajat { Nume="Tudor Pop",Vechime=2 }
};
IEnumerable<Angajat> query = an1.OrderBy(angajat => angajat.Vechime);
foreach (Angajat a in query)
{ Console.WriteLine("Nume= {0} vechime= {1} ani",
a.Nume,a.Vechime); }
Console.ReadLine();
}}
Salvati fila cu numele Iterator3.cs si compilati.
In acest exemplu se creaza colectia an1 direct din tipul IEnumerable<T>
si apoi se apeleaza metoda OrderBy() pentru a obtine sortarea elementelor
in functie de Vechime.
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class ProgramulMeu {
class Angajat { public string Nume { get; set; }
public bool Casatorit { get; set; } }
static void Main() { Angajat[] an1= {
new Angajat { Nume = "Maria Anton",Casatorit=false},
new Angajat { Nume = "Ion Ionescu",Casatorit=true },
new Angajat { Nume = "Mihai Popescu",Casatorit=true },
new Angajat { Nume = "Stefan Georgescu",Casatorit=true },
new Angajat { Nume = "Tudor Pop",Casatorit=false}
};
int nr1 = an1.Count( p => p.Casatorit == false);
int nr2 = an1.Count( p => p.Casatorit == true);
Console.WriteLine("{0} angajati sunt necasatoriti.",nr1);
Console.WriteLine("{0} angajati sunt casatoriti.",nr2);
Console.ReadLine();
}}
Salvati fila cu numele Iterator4.cs si compilati.
In acest caz se apeleaza metoda Count<TSource>(Func<TSource>,Boolean)
pentru a separa grupuri de elemente,in functie de un criteriu boolean.Se
observa ca interfata IEnumarable<T> este apelata implicit (prin Count()).
-54-
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class ProgramulMeu {
class Copil { public string Nume { get; set; } }
class Angajat { public string Nume { get; set; }
public Copil[] c1 { get; set; } }
static void Main(){
List<Angajat> an1 = new List<Angajat> {
new Angajat {
Nume = "Ion Ionescu", c1 = new Copil[]{ new Copil{ nume="Ion"}}},
new Angajat {
Nume = Mihai Popescu",c1 = new Copil[]{} },
new Angajat {
Nume = "Ana Pop",c1= new Copil[]{ new Copil{ Nume="Maria" },
new Copil{ Nume="Mihai" }}}
};
IEnumerable<string> nr = from Angajat in an1 where Angajat.c1.Any()
select Angajat.Nume;
foreach (string a in nr)
{ Console.WriteLine("Angajatul {0} are copii.",a); }
Console.ReadLine();
}}
Salvati fila cu numele Iterator5.cs si compilati.
Observati ca in acest caz,s-a utilizat o selectie LINQ,asemanatoare
cu o comanda de tip SQL.
Numarul de astfel de solutii,este practic infinit.Mecanismul de iterare
a unei colectii de date cu un astfel de iterator prezinta o serie intreaga
de avantaje:
1.-toate datele sunt incluse in obiecte,fiind astfel usor de manevrat in
memoria de operare,usor de gestionat si usor de eliberat din memorie
2.-metodele standard sunt simple,intuitive,usor de aplicat si pot fi
apelate direct (fara sa fie redefinite sau supraincarcate )
3.-iteratorii nu modifica resursa,nu adauga variabile suplimentare,nu
interfereaza cu alte date din sistem si utilizeaza tampoane de memorie
proprii,fara pointeri sau link-uri auxiliare
4.-datele sunt destul de bine securizate,deoarece un programator neavizat
nu poate exploata resursa,decat daca are acces la fila DLL si cunoaste
codul iteratorului.
Bineinteles ca executabilele pot fi deschise si studiate cu un utilitar
de genul ildasm.exe,dar este necesar ca programatorul sa detina notiuni
avansate de programare pentru a descifra codul,caz in care este foarte
putin probabil sa modifice negativ functionalitatea programului.
Interfetele necesare sunt arhivate in System.Collections.Generic si
respectiv in System.Linq.Are rost sa studiati intreaga lista de metode,
pentru fiecare interfata.Puteti sa formulati combinatii personalizate,sau
puteti sa preluati si transformati formule standard,prezentate in alte
exemple.Pentru formularea selectiilor de tip LINQ vezi si capitolul
urmator.
-55-
SELECTII LINQ
-58-
Un exemplu complet de selectie si reformatare a datelor va contine
obiecte cu mai multe proprietati si metode si o formula de selectare a
celor ce respecta o anumita conditie.
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
public class StudentClass { public string Nume { get; set; }
public List<int> Punctaj; }
protected static List<Student> students = new List<Student> {
new Student { Nume = "Albu Mihai",
Punctaj = new List<int>{ 79,82,81,79 }},
new Student { Nume = "Pop Maria",
Punctaj = new List<int>{ 99,76,90,94 }},
new Student { Nume = "Pop Victor",
Punctaj = new List<int>{ 93,72,80,87 }},
new Student { Nume = "Georgescu Lucia",
Punctaj = new List<int>{ 97,79,85,82 }},
new Student { Nume = "Ionescu Ion",
Punctaj = new List<int>{ 85,92,91,90 }},
new Student { Nume = "Barbu Constantin",
Punctaj = new List<int>{ 96,75,91,60 }}
};
public void Rezultate(int exam,int score) {
var selectie = from student in students
where student.Punctaj[exam] > score
select new { n1 = student.Nume,Nota = student.Punctaj[exam]};
foreach (var item in selectie)
{ Console.WriteLine("{0,-25}{1}",item.n1,item.Nota); }
}}
public class Program {
public static void Main() {
StudentClass sc = new StudentClass();
Console.WriteLine("Studentii promovati la primul examen sunt: ");
sc.Rezultate(0,90);
Console.WriteLine("Studentii promovati la al doilea examen sunt:");
sc.Rezultate(1,80);
Console.ReadLine();
}}
Salvati fila cu numele Linq5.cs si compilati.
In toate exemplele de mai sus,datele au fost preluate direct din
memoria de operare.In aplicatiile reale,datele sunt preluate din file
text,XML,HTML sau din tabele si baze de date.In acest caz,sunt necesare
si un set de operatii auxiliare: localizarea resursei in retea,crearea
unei conexiuni fixe cu resursa,deschiderea filei,citirea filei,conversia
sau reformatarea datelor,formarea de colectii primare...etc.
Tehnologia LINQ permite solutii extrem de versatile pentru orice tip
de data,preluat din orice tip de resursa.In plus,aceasta tehnologie
confera si un plus de siguranta in exploatare,daca formula de preluare
si selectie a datelor se arhiveaza intr-o fila DLL,la care utilizatorul
nu are acces direct.
-59-
NAMESPACES
-60-
Un astfel de bloc de memorie denumit,se incarca in memorie utilizand
cuvantul cheie "using".
EXEMPLU: using System;
incarca in memorie biblioteca DLL ce contine datele identificate prin
System.Datele incarcate cu using au vizibilitate doar in interiorul filei
din care s-a facut apelul (nu au vizibilitate in tot spatiul de memorie
alocat programului).Pentru a apela o clasa sau o structura din blocul de
memorie se utilizeaza operatorul punct (.) urmat de identificatorul
clasei respective:
EXENPLU: System.Console se refera la clasa Console din System.
Atunci cand blocul de memorie contine numeroase structuri de date
intricate,se poate crea o denumire alias,pentru a simplifica accesul la
date:
EXEMPLU: using m1 = Biblioteca1.Clasa1.Subclasa2.Metoda1;
in continuare se poate lucra in program cu identificatorul m1.
Daca un bloc de memorie contine mai multe spatii de memorie intricate,
apelul prin using,nu ofera acces la spatiile de memorie intricate ci doar
la datele din blocul respectiv de memorie.Pentru a avea acces si la
blocurile intricate,acestea vor trebui sa fie incarcate explicit:
EXEMPLU: using System;
using System.Collections;
Pentru a vizualiza mai usor aceasta situatie considerati ca fiecare
namespace este un folder.Pentru a avea acces la sub-foldere este necesar
sa se specifice calea completa de acces.
Exista doua feluri de namespaces:
1. -user defined -cele declarate de programator
2. -system defined - cele create automat de sistem,pentru controlul si
gestiunea interna a proceselor (au un cod alfanumeric aleator).
In mod normal,sistemul de operare elibereaza automat toate aceste
blocuri de memorie create automat,dar exista si numeroase situatii in
care sistemul este intrerupt inainte de a putea elibera memoria si
aceste blocuri de memorie paraziteaza memoria cu date inutile.Din acest
motiv,este recomandabil ca sistemul sa fie "deparazitat" (debugg) din
cand in cand.Exista si programatori care prefera sa creeze rutine speciale
prin care controleaza strict eliberarea memoriei,dupa fiecare executie.
Pentru a putea avea acces la intregul spatiu de memorie alocat unui
program se poate utiliza cuvantul cheie global.Orice referinta spre
spatiul global de memorie,sau spre un namespace se face prin operatorul
(::) in loc de (.).
Exemplu: global::System.Console.WriteLine("text");
sau class TestClass : global::TestApplicatie ;
Cu alte cuvinte,operatorul :: se utilizeaza pentru namespaces si pentru
spatiul global de memorie,in timp ce operatorul punct se utilizeaza pentru
tipurile de data sau membrii inclusi intr-un namespace (clase,structuri,
interfete....etc.).Cea mai frecventa utilizare pentru operatorul :: este
pentru a implementa o interfata.
Modul in care sunt gestionate spatiile de memorie,defineste cel mai
clar maturitatea si experienta unui programator.Nu se pot formula "reguli
de aur".Solutiile optime difera atat in functie de sistemul de operare si
configuratia hardware,cat si in functie de aplicatia propriu zisa : mono
sau multiprocesare,procesare paralela,networking...etc.
-61-
NULLABLE TYPES
-62-
Daca se compara doua date de tip nullable care au valoarea null,se va
returna valoarea True.
Operatorul ?? se utilizeaza pentru a specifica valoarea default ce se
returneaza atunci cand o valoare de tip nullable se atribuie unei date
de tip non-nullable.
EXEMPLU: int? x = null;
int? y = null;
int z = x ?? y ?? 0;
Unde ultima expresie se citeste astfel: z primeste valoarea lui x,sau
valoarea lui y,sau valoarea zero daca atat x cat si y sunt null.
Este esential sa fie desemnata o valoare default compatibila,atunci
se face conversia din tipul nullable in tipul ordinar.
Structura System.Nullable are urmatorii membrii: Nullable,HasValue,
Value,Equals,GetHashCode,GetType,GetValueOrDefault.Metodele se pot apela
pentru diverse operatii sau conversii:
EXEMPLU:
using System;
class ExempluTest {
public static void Main() {
float? a = 12.34f;
float? b = -1.0f;
Console.WriteLine("Valoarea versus valoarea default: ");
Display("A1",a,b);
b = a.GetValueOrDefault();
Display("A2",a,b);
a = null;
b = a.GetValueOrDefault();
Display("A3",a,b);
a = 12.34f;
b = -1.0f;
Console.WriteLine("Valoarea sau valoarea default atribuita: ");
Display("B1",a,b);
b = a.GetValueOrDefault(-222.22f);
Display("B2",a,b);
a = null;
b = a.GetValueOrDefault(-333.33f);
Display("B3",a,b);
Console.ReadLine();
}
public static void Display(string title,float? x,float? y)
{ Console.WriteLine("{0}) a= [{1}], b= [{2}]",title,x,y); }
}
Salvati fila cu numele Nullable2.cs si compilati.
In exemplul de mai sus,valoarea default atribuita a fost fie zero,daca
nu se specifica nici o valoare in GetValueOrDefault() fie o valoare data.
Tipul nullable este esential atunci cand o variabila primeste in
timpul executiei valori ce nu sunt intotdeauna definite.Prin acest tip
de data,se extinde foarte mult aria de executie a functiilor de cautare
a unui anumit tip de data,intr-o resursa,atunci cand rezultatul cautarii
este inpredictibil.Pentru detalii,consultati intreaga documentatie pentru
structura System.Nullable<T>.
-63-
UNSAFE CODE
-64-
La executie,puteti observa ca la ultima bucla,valoarea rezultata este
in afara domeniului de reprezentare,astfel ca valoarea returnata nu este
corecta.Un astfel de cod nesigur nu poate fi implementat decat intr-o
bucla UNSAFE.
Exemplul de mai sus,contine un simplu bloc de date "unsfe".Sunt insa
si situatii in care se declara o clasa intreaga unsafe:
EXEMPLU: using System;
unsafe class TestCaracter {
static void Main() {
char c1 = "|";
char* pChar = &c1;
void* pVoid = pChar;
int* pInt = (int*)pVoid;
Console.WriteLine("Caracterul este = {0}",c1);
Console.WriteLine("Adresa este = {0:X2}",(int)pChar);
Console.WriteLine("Valoarea pointerului = {0}",*pChar);
Console.WriteLine("Numeric = {0}",*pInt);
Console.ReadLine();
}}
Salvati ca Unsafe2.cs si compilati cu: csc /unsafe Unsafe2.cs
In acest exemplu,nu exista nici un cod nesigur,dar se utilizeaza mai
multi pointeri si operatii cu pointeri.Toate operatiile de acest gen,
trebuie incluse in bucle UNSAFE,pentru a atrage atentia depanatorului de
sistem,asupra unei eventuale cauze de malfunctie.Pentru a prelua adresa
unei expresii,se utilizeaza operatorul ampersand(&).Un alt exemplu tipic,
implica utilizarea pointerilor pentru a prelua valori de la o adresa:
EXEMPLU: using System;
class TestAdresa {
static void Main(){
int numar;
unsafe {
int* p = &numar;
*p = 0x2a3f;
Console.WriteLine(" p pointeaza valoarea: {0:X},*p);
Console.WriteLine(" la adresa: {0}",p->ToString());
}
Console.WriteLine("Numarul pointat este: {0}",numar);
Console.ReadLine();
}}
Salvati ca Unsafe3.cs si compilati cu: csc /unsafe Unsafe3.cs
In acest caz,codul este nesigur deoarece se poate atribui pentru *p
o valoare situata in afara domeniului de reprezentare pentru int.
(EXEMPLU: *p = 0xFFFFFFFF ).Daca valoarea este introdusa inainte de
compilare,va genera o eroare de compilare,dar daca este introdusa de
catre utilizator,in timpul executiei,va genera o eroare de executie.
Mai observati in acest exemplu,ca apelul unui membru al pointerului
se face prin operatorul -> in loc de punct. (Exemplu: p->ToString() )
Nu este obligatoriu ca un cod nesigur sa returneze date nereprezentabile.
Este suficient daca se creaza conditii potentiale pentru astfel de
situatii.Blocurile UNSAFE nu se utilizeaza de rutina,ci sunt o solutie
extrema,pentru situatii experimentale sau exceptionale.
-65-
DOCUMENTATIA XML
-66-
Semnificatia tag-urilor conventionale este urmatoarea:
<c> -semnaleaza ca textul include si o linie de cod din program
<para> -se utilizeaza in interiorul tag-urilor <summary>,<remarks>
sau <returns> pentru a structura textul
<see> -specifica un link in interiorul textului Ex: <see cref="text"/>
<code> -delimiteaza un fragment de cod inclus in text
<param> -indica numele parametrului unei functii <param name='numele'>
<seealso> -indica textul care doriti sa apara in sectiunea See Also din
documentul creat (creaza o lista de referinte bibliografice)
<example> -se utilizeaza pentru a delimita un exemplu de implementare a
codului explicat.Codul va putea fi extras si executat.
<paramref> -indica faptul ca textul din documentatie se refera la un
parametru de functie.Fila XML va putea sublinia acest cuvant,sau
va putea utiliza alt set de caractere (pentru evidentiere).
<sumarry> -se utilizeaza pentru a descrie o clasa sau un tip nou de data
<exception> -specifica tipul de exceptie returnat de bucla TRY...CATCH.Se
utilizeaza pentru metode,proprietati,evenimente si indexatori.
<permission> -documenteaza accesul la un membru oarecare al unei clase
<typeparam> -descrie tipul de data ce poate fi utilizat intr-un tip
generic,sau intr-o metoda cu parametru de tip generic
<include> -se utilizeaza pentru referinte la comentarii incluse in alte
file de documentare,ce descriu tipurile de data utilizate in
codul sursa.
Exemplu: <include file='numelefilei" path='tagpath[@name="id"]'>
<remarks> -introduce observatii si informatii auxiliare despre un tip
de data.Aceste informatii se afiseaza in Object Browser.
<typeparamref> -adauga informatii suplimentare pentru tipurile generice
<list> -se utilizeaza pentru a crea liste si tabele.Fiecare element
din lista va fi inclus intr-un tag de tip <item>
EXEMPLU: /// <summary> Acesta este un exemplu de lista
/// < list type="bullet">
/// <item>
/// <description> Item1 </description>
/// </item>
/// <item>
/// <description> Item2 </description>
/// </item>
/// </list>
/// </summary>
<returns> -se utilizeaza pentru a descrie valoarea returnata de o functie
<value> -descrie valoarea unei proprietati.
Daca utilizati si alte tag-uri decat cele recomandate,documentatia va
trebui sa contina si explicatia acestor tag-uri,pentru ca utilizatorul sa
le poata exploata eficient.Fila de documentare poate sa contina informatii
despre: clase,interfete,delegati,membrii clasei,evenimente,metode si
proprietati,tipuri de data definite de utilizator...etc.
Pentru ca fila de documentare sa poata fi citita cu utilitarele de tip
IntelliSense,trebuie ca numele filei .xml sa fie acelasi cu cel al filei
de asamblare (identic cu executabilul dar cu extensie diferita).
Fila de documentare este optionala,dar este recomandabila atunci cand
oferiti aplicatii ce pot fi transformate,adaptate,modernizate...etc.
-67-
STRUCTURA SI EXECUTIA APLICATIILOR
APPLICATION DOMAIN
-68-
EXEMPLU:
class Test {
static void Main() {
long mem1.mem2,mem3;
mem1 = System.GC.GetTotalMemory(false);
System.AppDomain dom1 =
System.AppDomain.CreateDomain("Domeniul1")';
dom1.ExecuteAssembley(@"c:\v3\Form1.exe");
System.AppDomain dom2 =
System.AppDomain.CreateDomain("Domeniul2");
dom1.ExecuteAssembley(@"c:\v3\Form2.exe");
mem2 = System.GC.GetTotalMemory(false);
System.Console.WriteLine("Memoria initiala = {0:F} bytes",mem1);
System.Console.WriteLine("Memoria dupa alocare: {0:F} bytes",mem2);
System.AppDomain.Unload(dom1);
System.AppDomain.Unload(dom2);
mem3 = System.GC.GetTotalMemory(false);
mem1 = mem2-mem3;
System.Console.WriteLine("Dupa eliberarea memoriei: {0:F}",mem3);
System.Console.WriteLine("Memoria eliberata= {0:F} bytes",mem1);
System.Console.ReadLine();
}}
Salvati fila cu numele Domain1.cs si compilati.
In exemplul de mai sus se pot remarca urmatoarele:
1. Biblioteca System nu este importata static si fiecare clasa este
apelata cu numele complet: System....
2. Se creaza doua obiecte de tip AppDomain.In fiecare astfel de obiect
se incarca si se executa cate un modul executabil,preluat de la o adresa
locala: c:\v3 (in exemplu v3 este un director local).
3. Se contorizeaza memoria totala ocupata in Garbagge Collector,inainte
si dupa eliberarea modulelor.
4. In timpul executiei,inchideti cele doua ferestre deschise automat,
pentru a permite continuarea executiei,apoi evaluati consumul de memorie.
Fiecare modul poate fi incarcat si descarcat independent,in momentul
dorit.La un calcul sumar,puteti observa ca memoria ocupata este mult mai
mare decat dimensiunea stricta a modulului.Ca rezultat,prin eliberarea
clasei AppDomain se elibereaza mai multa memorie decat volumul modulului.
Pentru orice alt gen de operatii cu si asupra modulelor,studiati cu
atentie metodele clasei AppDomain.
In concluzie,obiectele AppDomain prezinta urmatoarele avantaje:
1.-o eroare intervenita intr-o aplicatie nu poate afecta executia altor
aplicatii simultane.
2.-se exclude suprascrierea accidentala a unor resurse,de catre functii
executate intr-o aplicatie concomitenta
3.-fiecare aplicatie poate fi incarcata si executata in momentul dorit
4.-se poate controla exact consumul de memorie.
5.-se impiedeca accesul altor utilizatori din retea la datele din
aplicatie,chiar daca partajeaza acelasi spatiu de memorie cu utilizatorul
6.-un singur proces poate executa secvente de module executabile,in
conditii de maxima securitate in executie.
7.-creste flexibilitatea de lucru a serverului de retea
-69-
ATRIBUTELE C SHARP
[A3,A3,A3]
class ClasaDerivata{}
public class TestAtribute {
static void Main() {
ClasaDerivata d = new ClasaDerivata();
Console.WriteLine("Atributele clasei derivate sunt: ");
object[] attrs = d.GetType().GetCustomAttributes(true);
foreach(Attribute attr in attrs)
{ Console.WriteLine(attr); }
Console.ReadLine();
}}
Salvati fila cu numele Argument4.cs si compilati.In acest exemplu prin
argument se specifica faptul ca in clasele mostenitoare ale clasei A3 se
pot evalua argumente multiple identice.Pentru a sesiza diferenta,compilati
modulul fara atributul clasei A3.
In sinteza,atributele permit controlul fluxului de executie,sau
introduc metadate in program (asociate unui anumit bloc de date).
-72-
Containere de tip Collection
-73-
Pentru operatii mai complexe,se pot utiliza obiecte de tip ArrayList,
sau corespondentul lor de tip generic,reprezentat prin obiectele de tip
List<T>.Aceste obiecte au un set bogat de metode,prin care se pot face
operatii automate de sortare sau rearanjare a elementelor,dupa un anumit
algoritm.Cea mai frecventa utilizare,este pentru a prelua date dintr-o
resursa,pentru a le transfera apoi spre un obiect vizual de prezentare
sau prelucrare a lor (Exemplu: dintr-un tabel intr-un ComboBox).
EXEMPLU:
Creati un fisier de tip text,denumit Fructe.txt in care arhivati o
lista de fructe si pretul sau cantitatea lor:
mere 200 kg 3 lei/kg
cirese 130 kg 7 lei/kg
struguri 200 kg 9 lei/kg
Salvati fisierul pe unitatea C: si apoi editati un modul de genul:
using System;
using System.IO;
using System.Collections;
public class ExempluSortare {
public static void Main() {
ArrayList aria1 = new ArrayList();
string path = @"c:\Fructe.txt";
string[] readText = File.ReadAllLines(path);
foreach (string s in readText) { aria1.Add(s); }
aria1.Sort(0,aria1.Count,null);
Console.WriteLine("Datele preluate sunt: ");
Console.WriteLine("Numar: {0}", aria1.ount);
Console.WriteLine("Capacitate: {0}", aria1.Capacity);
Console.WriteLine("Valori:");
Editeaza(aria1);
Console.ReadLine();
}
public static void Editeaza ( IEnumarable lista1 ) {
foreach ( Object obj in lista1 )
Console.WriteLine(" {0}", obj);
}}
Salvati fila cu numele ArrayList1.cs si compilati.
Exemplul de mai sus,nu numai ca citeste datele din resursa,dar le si
sorteaza alfabetic inainte de a le procesa mai departe.Pentru sortare
s-a utilizat metoda implicita.Obiectul contine insa trei astfel de metode
supraincarcate,pentru a permite mai multe variante de abordare (Exemplu:
puteti crea un algoritm propriu pentru sortarea datelor,in functie de
unul sau mai multe criterii specifice.).
Incercati sa scrieti acelasi exercitiu,cu un obiect de tip List<T>.In
aplicatiile noi,este mai bine sa utilizati tipurile generice,deoarece
contin si coduri mai moderne si pot fi adaptate mult mai usor pentru a
crea aplicatii noi,sau pentru a exploata si alte resurse (ce contin
alt tip de date decat cel proiectat initial).
Elementele din lista ArrayList sunt indexate numeric,de la zero pana
la ultimul element din colectie.Pentru a selecta un element oarecare,se
poate utiliza numarul de indexare.Optional se pot face si operatii cu
grupuri de elemente.(Exemplu: de la elementul 10 pana la elementul 23 )
-74-
Pentru a dezvolta exercitiul de mai sus,sirurile preluate pot fi
separate cu metoda SPLIT,pentru a obtine subsiruri ce pot fi procesate
independent.De exemplu,cantitatea si pretul pot fi transformate in valori
numerice,pentru a le putea utiliza in operatii matematice.In acest mod,
o banala fila de tip text poate fi utilizata la fel ca si un tabel de
date.Chiar daca nivelul de securitate este foarte redus (datele sunt
foarte usor de corupt),acest mecanism simplu poate fi o alternativa pentru
datele de tip "open source".
Daca se utilizeaza List<T> in loc de ArrayList,exista si o metoda ce
permite conversia automata a datelor.
EXEMPLU:
using System;
using System.Drawing;
using System.Collections.Generic;
public class Exemplu {
public static void Main() {
List<PointF> lista1 = new List<PointF>();
lista1.Add(new PointF(15.7F,33.22F));
lista1.Add(new PointF(77.13F,15.3F));
lista1.Add)new PointF(1401.3F,8.1F));
Console.WriteLine();
foreach( PointF p in lista1)
{ Console.WriteLine(p); }
List<Point> lp = lista1.ConvertAll(
new Converter<PointF,Point>(Transformare));
Console.WriteLine("Dupa conversie:");
foreach( Point p in lp)
{ Console.WriteLine(p); }
Console.ReadLine();
}
public static Point Transformare(PointF pf)
{ return new Point(((int) pf.x),((int) pf.y)); }
}
Salvati fila cu numele Lista2.cs si compilati.
In exemplul de mai sus,o lista de puncte definite prin valori de tip
float,este convertita la o lista de puncte definite prin valori de tip
int.Se utilizeaza metoda standard ConvertAll().
Acest exemplu,ilustreaza destul de clar avantajul claselor generice
fata de cele tipizate.Aceeasi clasa de obiecte (List<T>) arhiveaza
initial elemente de tip PointF,apoi elemente de tip Point.
Pentru a putea efectua operatii mai complexe de sortare si filtrare
a datelor,au fost dezvoltate si clase de tip colectie in care fiecare
element este caracterizat printr-o pereche de variabile: una pentru cheia
de indexare si cea de a doua pentru valoarea propriu zisa.Acest gen de
obiecte seamana foarte mult cu tabelele organizate pe linii si coloane
si pot executa cu succes orice operatie de preluare si prelucrare a
datelor dintr-o baza de data (formeaza tamponul intermediar dintre baza
de date si aplicatie).
Exista numeroase exemple de astfel de clase,dar cele mai simple sunt
HashTable si corespondentul sau de tip generic:
Dictionary <TKey,TValue>
-75-
EXEMPLU:
using System;
using System.Collections.Generic;
public class Exemplu{
public static void Main() {
Dictionary<string,string> dic1 =
new Dictionary<string,string>();
dic1.Add("txt","notepad.exe");
dic1.Add("bmp","paint.exe");
dic1.Add("dib","paint.exe");
dic1.Add("rtf","wordpad.exe");
Console.WriteLine();
foreach( KeyValuePair<string,string> p1 in dic1)
{ Console.WriteLine("Extensie= {0},Program= {1}",p1.Key,p1.Value); }
Console.ReadLine();
}}
Salvati fila cu numele Dictionar1.cs si compilati.
Fata de obiectele de tip List<T>,aceste obiecte permit si o serie
de bucle de filtrare conditionate prin cheia de indexare.Aceasta cheie
de indexare,poate fi orice cod alfanumeric.
EXEMPLU:
in exercitiul de mai sus,se pot filtra toate elementele cu valoarea
"paint.exe",pentru a obtine extensia filelor produse,sau se pot filtra
toate elementele cu extensia >"mdb".Practic se lucreaza la fel ca si
in cazul tabelelor,unde elementele pot fi selectate fie in functie de
coloana din tabel,fie in functie de linia din tabel.
Daca necesitatile de programare o impun si nici una dintre clasele
standard nu satisface aceste necesitati,se pot declara si defini clase
noi de tip colectie,cu campuri de date,proprietati si metode specifice.
Nu este obligatoriu,dar este recomandabil ca si aceste clase nou definite
sa respecte sablonul general al claselor tip colectie,adica sa contina:
un enumerator,o metoda de clonare a datelor (CopyTo),capacitatea de a
numara elementele din container (Count),si una sua mai multe metode de
sincronizare a thread-urilor paralele.
Exista si clase de tip colectie specializate pentru un anumit tip
de date.
EXEMPLU: StringDictionary implementeaza o clasa de tip Hash Table in
care atat cheia de indexare cat si valoarea elementelor este strict de
tip string.
Alte clase de tip colectie se adapteaza la contextul de executie.
EXEMPLU: HybridDictionary class implementeaza o clasa ListDictionary
atat timp cat numarul de elemente arhivate este mic,dar se transforma
automat in Hash Table imediat ce numarul de elemente depaseste indexul
numeric( cel din domeniul de definitie initial ),practic schimba automat
metoda de indexare a elementelor.
Mai mult decat atat,o serie de obiecte vizuale,utilizeaza automat
obiecte de tip colectie,pentru a executa o serie oarecare de operatii.
EXEMPLU: Un obiect de tip ComboBox,va apela la un obiect de tip List<T>
sau Dictionary<T,T> pentru a-si incarca automat lista de optiuni,citind
datele dintr-o resursa oarecare (utilizeaza un link spre resursa).
Incercati sa editati o lista cu aplicatiile claselor de tip colectie.
-76-
EXCEPTII SI ERORI
-77-
Cel mai simplu exemplu este o eroare aritmetica generata de impartirea
prin zero.
EXEMPLU:
using System;
class Exceptie {
public static void Main() {
F1 f1 = new F1();
Console.Write(" 12/4 = ")'
int x = f1.Fractie(12,4);
Console.WriteLine(x);
Console.Write(" 12/0= ");
x= f1.Fractie(12,0);
Console.WriteLine(x);
Console.ReadLine();
}}
class F1 {
public int Fractie(int x,int y){
try { return (x / y); }
catch (System.DivideByZeroException dbz)
{ System.Console.WriteLine(dbz); return 0; }
}}
Salvati fila de mai sus cu numele Exceptie1.cs si compilati.Pentru a
observa diferenta,rescrieti exercitiul fara bucla TRY...CATCH.
In exemplul de mai sus,daca nu intervine exceptia se executa bucla TRY,
iar daca intervine exceptia se executa bucla CATCH.Acelasi mecanism se
putea obtine si cu un atribut conditional (vezi capitolul).
Exceptiile nu sunt intotdeauna identificate doar de sistemul de operare.
Daca este necesar,exceptiile si mesajele de eroare pot fi elibarate de
orice proces de executie cu o comanda THROW.Dupa eliberare,aceste exceptii
pot fi identificate si tratate la fel ca si cele eliberate de sistem.
EXEMPLU:
using System;
public class Exceptie2 {
static void Main() {
try {
Console.WriteLine("Se executa bucla TRY ! ");
throw new NullReferenceException();
}
catch (NullReferenceException e)
{ Console.WriteLine("EXCEPTIE= {0} ",e); }
finally
{ Console.WriteLine("Se executa bucla FINALLY !"); }
Console.ReadLine();
}}
Salvati fila cu numele Exceptie2.cs si compilati.
In exemplul de mai sus,se observa ca exceptia a fost emisa chiar din
interiorul buclei TRY...CATCH si a fost interceptata si tratata imediat.
In mod normal,o astfel de exceptie este emisa de un proces din alt modul,
sau chiar de un alt utilizator din retea,pentru a semnala o anumita
situatie,ce necesita o functie de tratare a exceptiei.
Exemplu: un anumit utilizator va fi exceptat de la acces la program.
-78-
Probabil ca exceptia cea mai frecevnta din sistem este generata de
absenta uneia dintre resursele apelate.De exemplu,un set oarecare de date
esentiale trebuie sa fie citite dintr-o fila de resurse,inainte de a fi
procesate.
EXEMPLU:
using System;
using System.IO;
public class ProcessFile{
public static void Main() {
FileStream fs = null;
try {
fs = new FileStream("Filadorita.txt",FileMode.Open);
StreamReader sr = new StreamReader(fs);
string line;
line = sr.ReadLine();
Console.WriteLine(line);
}
catch(FileNotFoundException e)
{
Console.WriteLine("[Fila cautata nu exista !] {0}",e);
}
finally
{
if (fs != null) fs.Close();
}
Console.ReadLine();
}}
Salvati fila cu numele Exceptie3.cs si compilati.
In exemplul de mai sus,se incerca deschiderea si citirea unei file
care nu exista in memorie.Bucla CATCH va intercepta eroarea de tip
FileNotFoundException si va executa blocul de tratare a exceptiei.Daca
adaugati o fila denumita Filadorita.txt,in care editati un text oarecare,
se va executa bucla TRY,apoi se va executa automat si bucla FINALLY,prin
care se inchide stream-ul si se elibereaza memoria.Daca fila ramane
deschisa,nu va mai putea fi apelata pana la inchiderea programului.Daca
fila respectiva este o resursa din retea,ea va ramane blocata pentru toti
ceilalti utilizatori,pana la terminarea programului.Din acest motiv,este
esential ca atunci cand se citesc date din file de resurse,sa se adauge
si o bulca automata pentru inchiderea filei.
Emiterea,interceptarea si tratarea erorilor se poate face si pentru
situatii exceptionale,in care doriti sa implementati un anumit artificiu
de programare,sau doriti sa subliniati un anumit aspect.Totusi nu este
recomandabil sa se abuzeze de acest mecanism.Fiecare exceptie si bucla de
interceptare a exceptiei,fragmenteaza executia,introduce cel putin inca
un thread suplimentar,adauga seturi intregi de operatii si intarzie
executia programului.Din acest motiv,este recomandabil ca situatiile ce
pot genera exceptii sa fie evitate atunci cand este posibil si sa fie
tratate doar atunci cand nu pot fi evitate prin alt procedeu.Exista si
programatori analisti,care construiesc un sistem complex de depanare
automata a programelor,dar cu pretul unui consum nejustificat de memorie
suplimentara.Decizia finala apartine fiecarui programator.
-79-
THREADS (Fire de executie)
-80-
EXEMPLU:
using System;
using System.Threading;
class Thread1 {
public class Worker {
public void Fir1(){
AppDomain dom1 = AppDomain.CreateDomain("d1");
dom1.ExecuteAssembley(@"c:\v3\Clasa1.exe");
AppDomain.Unload(dom1);
}
public void Fir2(){
AppDomain dom2 = AppDomain.CreateDomain("d2");
dom2.ExecuteAssembley(@"c:\v3\Clasa3.exe");
AppDomain.Unload(dom2);
}}
static void Main(){
Worker fir1 = new Worker();
Thread t1 = new Thread(fir1.Fir1);
t1.Start();
Worker fir2 = new Worker();
Thread t2 = new Thread(fir2.Fir2);
t2.Start();
}}
Salvati fila cu numele Thread2.cs si compilati.
In exemplul de mai sus,Clasa1.exe si Clasa3.exe sunt doua module
executabile create anterior (vezi capitolul clase).Se observa ca pentru
a crea un thread,constructorul are ca parametru o functie,adica functia
ce urmeaza sa fie executata in acel stream.Pentru ca modulele sa fie
complet independente,am inclus in functia fiecaruia dintre ele toate
operatiile necesare,inclusiv cele de eliberare a memoriei.Ca rezultat,
cele doua ferestre lansate in executie,se comporta ca si cand ar fi
doua programe complet independente.Lansarea in executie a unui thread
se face prin metoda Start(),iar intarzierea executiei se poate face prin
metoda Sleep().
In exemplul de mai sus,ambele thread-uri sunt declarate identic,asa
ca sunt procesate in ordinea in care apar in program.In mediul real de
executie Windows,procesorul lucreaza simultan cu cateva mii de astfel
de thread-uri (vezi fereastra Windows Task Manager).Atunci cand memoria
de operare este la limita,sistemul de operare va elimina o parte dintre
aceste thread-uri de la executie si le va transfera intr-un tampon de
memorie auxiliara,unde stau "pe linie de asteptare",pana cand pot fi
reluate spre executie.Acest proces se executa in functie de ordinea in
care au fost trimise spre procesor si in functie de dimensiunea fiecarui
thread.Pentru a controla acest proces,se pot seta prioritati de executie
pentru thread-urile esentiale,cu ajutorul proprietatii Thread.Priority.
Exista cinci grade de prioritate: Highest,AboveNormal,Normal,BelowNormal,
si Lowest.Pentru a simplifica executia,thread-urile care contin operatii
esentiale,cum ar fi destructorii si eliberarea memoriei,vor fi setate cu
prioritatea cea mai mare (Highest),iar cele care contin doar operatii
optionale sau neimportante,vor fi setate cu prioritatea cea mai mica.
-81-
O alta proprietate extrem de utila este Thread.ThreadState.Daca se
citeste aceasta proprietate in diferite momente ale executiei,se poate
afla status-ul de moment al firului de executie.Aceste volori pot fi:
Aborted,AbortRequested,Background,Running,Stopped,StopRequested,Suspended,
SuspendRequested,Unstarted sau WaitSleepJoin.Cunoasterea status-ului
pentru fiecare thread este esentiala in etapa de depanare.
EXEMPLU:
using System;
using System.Threading;
-82-
O situatie particulara apare atunci cand mai multe fire de executie,
exploateaza acelasi obiect (sau fila de resurse).Daca un fir de executie
intrerupe operatiile executate de alt fir de executie,pentru a castiga
accesul la resurse,atunci primul fir de executie va fi invalid,sau chiar
mai mult decat atat,va contine date invalide.Pentru a evita acest gen de
situatii,este necesara sincronizarea firelor de executie,astfel incat la
un anumit moment dat,un singur thread sa poata accesa obiectul sursa.
Cea mai simpla sincronizare se face cu ajutorul unui obiect de tip
Monitor,care prin metoda Enter() blocheaza resursa si prin metoda Exit()
elibereaza resursa.
EXEMPLU:
using System;
using System.Threading;
using System.Collections;
public class T1 {
public static Queue stiva1 = new Queue();
public static void Proces1() {
for (int i = 0; i < 10; i++) {
Monitor.Enter(stiva1);
stiva1.Enqueue("text");
Console.WriteLine("Proces1 - operatia: {0}", i);
Monitor.Exit(stiva1);
Thread.Sleep(250);
}}
public static void Proces2() {
for (int i = 0; i < 10; i++) {
Monitor.Enter(stiva1);
stiva1.Enqueue("text");
Console.WriteLine("Proces2- operatia: {0}", i);
Monitor.Exit(stiva1);
Thread.Sleep(150);
}}
public static void Proces3() {
for (int i = 0; i < 10; i++) {
Monitor.Enter(stiva1);
stiva1.Enqueue("text");
Console.WriteLine("Proces3- operatia: {0}", i);
Monitor.Exit(stiva1);
Thread.Sleep(100);
}}
public static void Main() {
Thread t1 = new Thread(new ThreadStart(Proces1));
t1.Start();
Thread t2 = new Thread(new ThreadStart(Proces2));
t2.Start();
Thread t3 = new Thread(new ThreadStart(Proces3));
t3.Start();
t1.Join();t2.Join();t3.Join();
Console.ReadLine();
}}
Salvati fila cu numele Thread4.cs si compilati.
-83-
In exemplul de mai sus,fiecare thread blocheaza stiva,introduce o
valoare si apoi deblocheaza stiva.Cele trei thread-uri au intarzieri
diferite,astfel ca primul dintre ele termina operatiile cu mult in urma
celorlalte doua.Atunci cand doua thread-uri incerca sa acceseze stiva
simultan,cel care ajunge al doilea,trebuie sa astepte pana cand primul
fir elibereaza accesul.
Exista si alte clase destinate pentru sincronizare: Mutex,Interlocked,
AutoResetEvent,ManualResetEvent...etc.Alegerea solutiei de sincronizare
depinde de natura programului.
In principiu,intr-o aplicatie exista un fir de executie principal,
utilizat pentru a controla fluxul de executie al tuturor celorlalte fire
de executie si anume cel care executa functia Main().Pe langa acesta,mai
pot exista unul sau mai multe thread-uri ce executa in background operatii
de intretinere si control si unul sau mai multe fire de executie in care
ruleaza interfata vizuala cu utilizatorul (una sau mai multe ferestre).
Este esentiala eliberarea memoriei,imediat ce firele de executie nu
mai sunt utile.Pentru a forta operatia de "garbage collection" se poate
apela metoda GC.Collect().
EXEMPLU:
using System;
using System.Threading;
using System.Collections.Generic;
class Garbage{
static void Main() {
Garbage.MakeSomeGarbage();
Console.WriteLine(" Total memorie consumata: {0}",
GC.GetTotalMemory(false));
GC.Collect();
Console.WriteLine(" Dupa eliberarea memoriei: {0}",
GC.GetTotalMemory(true));
Console.ReadLine();
}
static void MakeSomeGarbage() {
Array a1 = Array.CreateInstance(typeof(Thread),2000);
for(int i = 2; i < 1900); i++)
{ a1.SetValue(new Thread(MakeSomegarbage),i); }
}}
Salvati fila cu numele Garbage.cs si compilati.
In exemplul de mai sus,se masoara memoria dupa crearea unei arii de
thread-uri goale,si apoi dupa fortarea eliberarii memoriei prin Collect().
Thread-urile multiple creaza impresia de procesare paralela.Modul in
care se organizeaza si sistematizeaza firele paralele de executie,denota
experienta si personalitatea programatorului.Nu se pot formula sfaturi
generale,dar in mod sigur este bine sa alegeti solutiile prin care se
consuma cat mai putina memorie pentru fiecare operatie,iar blocurile mari
de memorie se impart in blocuri cat mai mici.De cele mai multe ori,este
foarte practic ca principalele functii ale programului sa fie impartite
in module.Nu numai ca se grabeste executia,dar modulele vor putea fi
utilizate sau adaptate si pentru alte programe si aplicatii.Daca oferiti
aceste module si altor programatori,este bine sa fie insotite si de o
documentatie cat mai clara si de filele de resurse necesare.
-84-
REFLEXIE
[AtributulMeu,AtributulMeu]
[System.Obsolete("Aceasta clasa este perimata !",true]
class ClasaMea {
public static void Main() {
System.Reflexion.MemberInfo info = typeof(ClasaMea);
Console.WriteLine("Atributele pentru ClasaMea sunt:");
for (int i = 0; i < attributes.Length; i++)
{ Console.WriteLine(attributes[i]); }
Console.ReadLine();
}
}
Salvati fila cu numele Reflexie3.cs si compilati.
In exemplul de mai sus,se citesc atributele asociate clasei ClasaMea.
Acest gen de operatie,poate fi utila in etapa de autodepanare si control
pentru a identifica o exceptie generata de unul dintre atributele clasei
intr-un anumit context de executie.Pentru verificare,se executa clasa
fara atributul respectiv,sau se corecteaza mediul de executie.
-86-
Tot prin reflexie se poate invoca si o metoda a unui tip de data din
program,fara a mai crea o instanta (un obiect).
EXEMPLU:
using System;
using System.Reflection;
class Exemplu {
static void Main(){
Type t = typeof(String);
-87-
SECURITATEA APLICATIILOR
-88-
EXEMPLU: -producatorii de spam-uri,utilizeaza motoare automate de cautare
pentru a identifica si prelua orice adresa e-mail din paginile de tip
HTML,din reteaua Web.Pentru a evita aceste motoare,se pot inlocui toate
adresele e-mail din site cu pictograme in care adresa este scrisa intr-o
resursa de tip grafic (exemplu o imagine editata cu Paint).
Masurile de securitate pot interesa doar un obiect sau o interfata
grafica,sau pot interesa intreaga aplicatie.De exemplu,pentru a securiza
doar un text,sau un fragment de text,se poate utiliza clasa SecureString
din Sistem.Security:
EXEMPLU:
using System;
using System.Security;
-89-
O data introduse in mediul de memorie,aceste permisiuni vor reglementa
accesul la un anumit tip de resurse.In exemplul de mai sus,permit citirea
celor doua variabile: "USERNAME" si respectiv "COMPUTERNAME".
O alta situatie foarte frecventa o reprezita accesul la resurse din
afara aplicatiei,in special la file si fisiere.Pentru aceste operatii,se
pot utiliza clasele din System.Security.AccesControl:
EXEMPLU:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.AccesControl;
namespace ConsoleApplication1 {
class Program{
static string fila1;
static void Main(string[] args){
fila1 = "csc.exe";
try { using (FileStream st1 = new FileStream(fila1,
FileMode.Open, FileAccess.Read))
{ FileSecurity sec1 = st1.GetAccessControl();
foreach (FileSystemAccessRule lege1 in
sec1.GetAccessRules(true,true,
typeof(System.Security.Principal.NTAccount)))
{ Console.WriteLine("{0}{1} acces tip {2} pentru {3}",fila1,
lege1.AccessControlType == AccessControlType.Allao ?
"permite": "blocheaza",lege1.FileSystemRights,
lege1.IdentifyReference.ToString());
}}}
catch { Console.WriteLine("Adresa incorecta !"); }
Console.ReadLine();
}}}
Salvati fila cu numele Security3.cs si compilati.
In exemplul de mai sus,se citesc atributele compilatorului csc.exe
din directorul curent.In mod asemanator,se pot citi sau edita toate
restrictiile de acces la fisier sau fila.Observati ca accesul este
nerestrictionat pentru administratorul de sistem,pentru autoritatea NT si
pentru userul care administreaza unitatea (administrator de sistem).
Prin acest mecanism,se poate restrictiona accesul la unele fisiere,
fie pentru copii,fie pentru persoanele neautorizate.Drepturile de acces
si sau audit al filei,se concretizeaza sub forma unor "legi" (rules)
salvate sub forma de obiecte de tip FileSystemAccesRule.Asemanatoare cu
FileSecurity este clasa FileSystemSecurity destinata pentru a putea
reglementa accesul la filele de tip sistem.
O situatie particulara se intalneste in cazul aplicatiilor de retea
tip Web,in care trebuie sa existe o polita de evidenta prin care se
specifica explicit drepturile de acces la o anumita resursa.Exemplu cel
mai clar,este pentru filele de tip applet,care nu pot avea acces la
resursele sistemului,decat daca exista o polita explicita.Aceasta polita
se poate edita manual,intr-o fila text,se poate edita cu instrumnete
specializate,sau se poate utiliza un obiect de tip System.Policy.
-90-
Daca un astfel de obiect reglementeaza mai multe astfel de reguli de
acces,se poate utiliza un obiect de tip Evidence,pentru a organiza aceste
seturi de legi.
EXEMPLU:
using System;
using System.Collections;
using System.Security;
using System.Security.Policy;
using System.Security.Permissions;
using System.Globalization;
-91-
PLATFORMA .NET
-92-
Instalarea platformei .NET este foarte facila.Se descarca din reteaua
Internet un modul executabil,denumit dotnetfx,ce va executa automat toate
operatiile necesare.In urma instalarii,vor exista cele doua componente
distincte: mediul de executie CLR si biblioteca de clase .NET Framework
Class Library.Cele doua componente,contin urmatoarele elemente esentiale:
-93-
Pe langa compilatoarele JIT,CLR contine si un alt mecanism pentru
conversia codurilor MSIL in cod masina,denumit "install-time",adica cel
generat in momentul instalarii.Prin acest mecanism,codul MSIL este
convertit complet in cod masina,in momentul instalarii in memorie si
apoi este arhivat sub forma de coduri masina.In momentul apelului,codul
masina va fi expediat spre procesor,crescand mult viteza de executie.
Acest mecanism este utilizat doar atunci cand executia nu depinde de
configuratia actuala a mediului de executie si poate fi utilizat mediul
de executie din momentul initial.
Interpretorul CLR contine si un mecansim de verificare si control,prin
care verifica daca operatiile executate nu vor genera erori de executie.
Exemplu: -daca doua sau mai multe module executate simultan solicita
operatii ce duc la suprascrierea unor adrese de memorie sau la operatii
incompatibile.Acest mecanism va verifica si politele de securitate
anexate fiecarui modul,pentru a impiedeca orice operatie neautorizata.
Toate exceptiile si erorile vor fi semnalate prin mesaje de eroare,
dublate sau nu de blocarea executiei,in functie de gravitatea erorii.
In sinteza,CLR asigura urmatoarele operatii: incarcarea si conectarea
resurselor software,verificarea erorilor,exceptiilor si a restrictiilor,
compilarea la cod masina,gestionarea memoriei,eliberarea automata a
memoriei si securizarea executiei prin contentionarea aplicatiei.
-94-
Numarul de containere (namespaces) din biblioteca de clase,difera in
functie de versiunea .NET incarcata.Fiecare versiune noua,adauga seturi
noi de clase,grupate in containere diferite.Majoritatea lor sunt incluse
in cele doua mari containere radacina: Microsoft si System.Exista si
cateva containere izolate: Accessibility,UIAutomationClientsideProviders
si XamlGeneratedNamespace.Clasele generate de compania Microsoft sunt
grupate in containerul Microsoft.Exemple: Microsoft.Build.BuildEngine,
Microsoft.CSharp,Microsoft.JScript,Microsoft.VisualBasic,Microsoft.Win32.
Restul claselor sunt incluse in containere ce au ca radacina comuna
containerul System.Exemple: System.Activities,System.AddIn,System.CodeDom,
System.Collections,System.ComponentModel,System.Configuration,System.Data,
System.Diagnostics,System.Drawing,System.IO,System.Management,System.Net,
System.Printing,System.Redflection,System.Runtime,System.Security,System.
ServiceModel,System.Speech,System.Text,System.Web,System.Windows,System.
Workflow,System.Xaml,System.Xml...etc.Fiecare dintre aceste subcontainere
radacina,poate contine la randul sau alte subcontainere.Este important sa
cunoasteti localizarea fiecarei clase,in containere pentru a putea preciza
calea de acces completa.Daca se importa doar containerul radacina,toate
clasele din subcontainere nu vor avea vizibilitate in program.Din acest
motiv este esential sa fie importat si subcontainerul ce contine clasa.
EXEMPLU: clasa ArrayList este inclusa in System.Collections
pentru a putea fi utilizata in program,se va utiliza expresia:
System.Collections.ArrayList sau se va importa intregul container cu:
using System.Collections; //apoi se poate utiliza direct ArrayList
Daca se importa doar containerul System,ArrayList va fi nedefinita.
Biblioteca de clase .NET contine un numar enorm de clase si resurse.
Prezentarea lor este imposibil de facut intr-un singur manual.Referintele
bibliografice se gasesc in reteaua Intenet,in biblioteca MSDN,sau pentru
utilizatorii programului Visual C# pot fi verificare cu utilitarul denumit
Intelisense.
Nu exista reguli speciale pentru limbajul C#.Regulile de baza sunt
cele definite pentru programarea orientata spre obiect.Aceleasi clase,
vor fi importate si in mediu C# ca si in mediul Visual Basic.Exista doar
mici diferente in formarea expresiilor,specifice fiecarui limbaj,dar
aceste diferente dispar o data cu compilarea la limbaj intermediar.
System.Windows.Forms
-95-
EXEMPLU:
using System;
using System.Windows.Forms;
public class AWindow : System.Windows.Forms.Form {
public AWindow(){ }
static void Main(){
Application.Run(new AWindow());
}}
Salvati fila cu numele Window1.cs si compilati.Exemplul de mai sus,
creaza o fereastra simpla de tip Form.Aceasta fereastra poate constitui
suportul pentru diverse alte operatii: se poate scrie la fel ca si in
fereastra Console,se pot desena grafice sau se pot adauga alte obiecte
si componente vizuale.
EXEMPLU:
using System;
using System.Windows.Forms;
using System.Drawing;
public partial class AWindow: System.Windows.Forms.Form{
public AWindow() { Initializeaza(); }
private void Initializeaza() {
Button1 buton1 = new Button();
buton1.Text = "OK";
buton1.Location = new Point (90,100);
buton1.Size = new Size (72,24);
buton1.Click += new EventHandler (button_Click);
this.Controls.AddRange( new Control[] { buton1 });
this.ShowDialog();
}
private void button_Click(object sender, EventArgs e) {
MessageBox.Show("Butonul meu functioneaza OK !");
}
static void Main(){
Application.Run(new AWindow());
}}
Salvati fila cu numele Window2.cs si compilati.Observati in acest
exemplu ca in constructorul ferestrei am adaugat o functie de initializare
prin care de adauga controalele necesare si un eventhandler ce gestioneaza
evenimentul OnClick al butonului buton1.Fereastra functioneaza in acest
caz pe post de container pentru toate celelalte controale auxiliare.Pentru
a include butonul in fereastra se utilizeaza metoda Controls.AddRange.
Functia de initializare contine si toate setarile si modificarile de
design dorite.Nu este esential,dar este recomandabil ca functia de
initializare ca contina un nume cat mai sugestiv,pentru a fi cat mai
usor de identificat si verificat in etapa de depanare.Daca programul este
verificat si depanat cu programe automate,acestea vor executa initial
constructorul si functiile incluse in constructor si apoi vor verifica
mediul de executie,inainte de a trece la executarea celorlate metode si
functii din program.Conventional se utilizeaza numele:
InitializeComponent()
dar se poate utiliza orice denumire sugestiva.
Butonul se poate utiliza pentru a declansa functiile programului.
-96-
Fereastra poate fi utilizata si ca suport grafic,pentru operatii de
scriere sau desen.In acest caz,in locul unei functii de initializare,se
va defini o metoda OnPaint() in care se includ operatiile dorite:
EXEMPLU:
using System;
using System.Drawing;
using System.Windows.Forms;
public class AWindow: System.Windows.Forms.Form {
public AWindow(){}
protected override void OnPaint( PaintEventArgs paintEvent)
{
Graphics g = paintEvent.Graphics;
SolidBrush brush = new SolidBrush( Color.Blue );
Pen pen = new Pen( Color.Red );
g.FillRectangle( brush, 90, 30, 150, 90 );
g.DrawLine( pen, 90, 30, 110, 40 );
g.DrawLine( pen, 90, 120, 110, 130 );
g.DrawLine( pen, 240, 30, 260, 40 );
g.DrawLine( pen, 240, 120, 260, 130 );
g.DrawRectangle( pen, 110, 40, 150, 90 );
}
static void Main(){
Application.Run(new AWindow());
}}
Salvati fila cu numele Window3.cs si compilati.Observati etapele
comune pentru orice aplicatie grafica: se creaza contextul grafic,apoi
se creaza un obiect de tip penita (pentru desen) si un obiect de tip
pensula (pentru umplerea suprafetelor) dupa care se executa operatiile
dorite,specificand coordonatele din fereastra.Atunci cand fereastra
contine atat controale vizuale,cat si reprezentari grafice,este
recomandabil sa utilizati containere de tip Panel,in care sa separati
controalele vizuale de partea grafica.In caz contrar,exista riscul de
a suprapune obiectele vizuale cu graficele,sau de a altera coordonatele
obiectelor...etc.
O situatie mai speciala,este atunci cand reprezentarile grafice se
creaza si se modifica dinamic.In acest caz,este recomandabil sa existe
o fereastra separata pentru aceste reprezentari grafice.Preferabil se
vor utiliza doua sau mai multe thread-uri,astfel incat fereastra grafica
sa poata functiona in paralel cu fereastra in care sunt incluse butoanele
de control.
O interfata grafica cu utilizatorul este formata din mai multe astfel
de obiecte vizuale,a caror functinalitate este conectata intre ele prin
metode,proprietati,sau schimb activ de date.Proiectarea unui interfete
consta din urmatoarele etape:
1. -alegerea obiectelor necesare
2. -conectarea lor functionala pentru scopul propus
3. -aranjarea obiectelor in fereastra
4. -setarea designului grafic: culori,dimensiuni,etichete...etc.
5. -testarea exceptiilor si a cazurilor extreme
6. -depanare,verificare si control
7. -testarea interfetei pentru cativa utilizatori neavizati
-97-
EXEMPLU:
using System;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
-100-
Obiectele din interfata se pot conecta si la resurse externe,fie pentru
a prelua,fie pentru a prelucra si arhiva datele.
EXEMPLU: -creati o fila text denumita Fructe.txt si editati o lista de
fructe pe mai multe randuri.Apoi utilizati aceasta fila pentru a popula
cu valori lista de optiuni a unui obiect de tip ComboBox:
using System;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
-101-
O alta fereastra independenta poate prezenta un calendar complet cu
ajutorul unui obiect de tip Month.Calendar.
EXEMPLU:
using System;
using System.Drawing;
using System.Windows.Forms;
-102-
EXEMPLU:
using System;
using System.Windows.Forms;
using System.Drawing;
-103-
System.Data
-104-
Principalele obiecte sunt: DataSet,DataTable,DataColumn si DataRow.
Afisarea datelor se poate face cu un obiect DataGrid (din Windows.Forms).
Tabelul se poate popula su cu date preluate dintr-o fila de tip text,cum
este cea creata pentru exemplul Window9.cs (vezi Fructe.txt).
EXEMPLU:
using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
public class Form1 : System.Windows.Forms.Form {
private System.ComponentModel.Container components;
private DataGrid Grila1;
private DataSet Info1;
public static void Main() { Application.Run(new Form1()); }
public Form1() { InitializeComponent();
SetUp(); }
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.Grila1 = new DataGrid();
this.Text = "Exemplu de grila: ";
this.ClientSize = new System.Drawing.Size(350,350);
Grila1.Location = new Point(24,50);
Grila1.Size = new Size(300,200);
Grila1.CaptionText = "Grila pentru afisaj: ";
this.Controls.Add(Grila1); }
private void Setup() {
Info1 = new DataSet("Info1");
DataTable tabel1 = new DataTable("Fructe");
DataColumn nume = new DataColumn("Nume",typeof(string));
nume.MaxLength = 80;
tabel1.Columns.Add(nume);
Info1.Tables.Add(tabel1);
DataRow newRow1;
string path = @"c:\Fructe.txt";
string[] readtext = File.ReadAllLines(path);
foreach (string s in readText)
{ newRow1 = tabel1.NewRow();
newRow1["Nume"] = s;
table1.Rows.Add(newRow1); }
Grila1.SetDataBinding(Info1,"Fructe");
DataGridTableStyle ts1 = new DataGridTableStyle();
ts1.MappingName = "Fructe";
ts1.AlternatingBackColor = Color.Yellow;
DataGridColumnStyle coloana = new DataGridTextBoxColumn();
coloana.MappingName = "Nume";
coloana.Width = 400;
ts1.GridColumnStyles.Add(coloana);
Grila1.TableStyles.Add(ts1); }}
Salvati fila cu numele Window14.cs si compilati.
-105-
Obiectul pentru afisarea datelor de tip DataGrid este rudimentar.Pentru
a putea organiza designul vizual,este necesar un set de obiecte speciale
cum sunt: DataGridTableStyle,DataGridColumnStyle si DataGridView (toate
sunt incluse in System.Windows.Forms).
Bibliotecile Data.Odbc si Data.OleDb contin alte clase auxiliare.De
exemplu,clasa OleDbDataAdapter faciliteaza preluarea datelor dintr-un
tabel,cu o comanda SQL si apoi transferul acestor date intr-un obiect de
tip DataTable.
EXEMPLU:
using System;
using System.IO;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Data.OleDb;
public class Form1 : System.Windows.Forms.Form {
private System.ComponentModel.Container components;
private DataGrid Grila1;
private DataSet Info1;
public static void Main() { Application.Run(new Form1()); }
public Form1(){ InitializeComponent();
SetUp(); }
private void InitializeComponent(){
this.components = new System.ComponentModel.Container();
this.Grila1 = new DataGrid();
this.Text = "Exemplu de grila: ";
this.ClientSize = new Size(650,450);
Grila1.Location = new Point(24,50);
Grila1.Size = new Size(500,300);
Grila1.CaptionText = "Grila pentru afisaj:";
this.Controls.Add(Grila1); }
private void SetUp() {
System.Data.OleDbConnection con;
con = new System.Data.OleDb.OleDbConnection("");
con.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;
DataSource=Biblioteca.mdb";
con.Open();
Info1 = new DataSet("Info1");
DataTable tabel1 = new DataTable("Carti");
OleDbDataAdapter sda = new OleDbDataAdapter("SELECT * FROM Carti,con);
sda.Fill(tabel1);
DataColumn nume = new DataColumn("Nume",typeof(string));
tabel1.Columns.Add(nume);
Info1.Tables.Add(tabel1);
Grila1.SetDataBinding(Info1,"Carti");
con.Close();
}}
Salvati fila cu numele Window15.cs si compilati.In exemplul de mai
sus,datele sunt preluate din baza de date denumita Biblioteca.mdb ce
contine un tabel denumit Carti (din directorul curent).
-106-
Pentru a putea lucra cu tabele si baze de date,trebuie sa existe un
driver inregistrat.Pentru bazele de date tip Microsoft,sistemul XP
contine dirver-ul implicit "Microsoft.Jet.OLEDB.4.0".Puteti insa sa
utilizati orice alt driver daca este inregistrat in sistemul de operare.
EXEMPLE:
"Provider=MSDAORA; Data Source=ORACLE8i7;"
"Provider=SQLOLEDB; Data Source=(local);"
Driver-ul impreuna cu locatia bazei de date formeaza string-ul de
conectare necesar pentru a deschide o conexiune intre aplicatia si baza
de date locala.Dupa ce conexiunea este stabilita corect,se poate crea un
obiect de tip OleDbDataAdapter ce preia toate datele din tabel in functe
de comanda SQL din constructor.Pentru a putea transfera aceste date in
DataTable,cu ajutorul metodei Fill(),este necesar ca tabelul ca contina
cel putin o coloana predefinita (prima coloana sa nu fie nula).Acest
mecanism este foarte operativ si permite filtrarea datelor prin comanda
SQL.Obiectul OleDBDataAdapter nu poate transfera insa decat un singur
tabel per operatie si nu permite operatii de intersectare sau reuniune a
doua tabele.
Atunci cand baza de date este localizata intr-un server,string-ul de
conectare trebuie sa contina in plus si numele serverului,adresa de
acces si parola utilizatorului.Operatiile cu baze de date din serverul
local,sau din retea sunt insa recomandate doar programatorilor avansati.
Tabelele si bazele de date contin de cele mai multe ori volume mari
de informatie,ce necesita operatii cu volume mari de memorie.Cea mai mica
gresala de conceptie,poate avea efecte dezastroase asupra executiei.Din
acest motiv,este recomandabil sa utilizati formule de lucru standardizate,
sau sa preluati si sa adaptati exemple pre-existente.Principalele operatii
de selectare si filtrare a datelor se pot organiza cu ajutorul comenzilor
SQL,fara sa mai fie necesar sa programati algoritmi speciali.
Exista numeroase obiecte de tip colectie,ce pot prelua si prelucra
seturi sau subseturi de date din tabele.Exemplu: coloanele unui tabel
se pot utiliza pentru a completa optiunile unor casete de tip ComboBox,
sau ListBox.Uneori,se selecteaza din tabel,o singura inregistrare ce
poate fi transformata in string si afisata cu un obiect TextBox sau Label.
De cele mai multe ori,este nepractic sa prezentati intregul tabel sub
forma de grila.Nu numai ca se consuma volume inutile de memorie,dar se
prezinta si date ce nu sunt relevante pentru utilizator (sau sunt chiar
secrete).Din acest motiv,solutia de prezentare a datelor procesate va fi
personalizata in functie de aplicatie si utilizator.
Acelasi mecanism functioneaza si in sens invers.Datele din unul sau
mai multe obiecte vizuale din interfata grafica,pot fi arhivate in tabele
si baze de date.Nu se justifica insa crearea de tabele,decat atunci
cand se vor executa si operatii de sortare,selectare sau filtrare a lor.
Pentru arhivare simpla,se vor prefera intotdeauna fisierele de tip text,
deoarece ocupa mult mai putin spatiu de memorie si nu necesita operatii
de formatare,conversie,criptare...etc.
Bazele de date si tabelele,sunt un instrument de lucru foarte practic,
dar reduc viteza de executie si intoduc un numar foarte mare de operatii
suplimentare.In plus,coruperea accidentala a unei baze de date este
mult mai greu de corectat si depanat decat o fila de tip text.Solutia
optima este sa pastrati intotdeauna copii de siguranta.
-107-
System.Timers
-108-
System.Runtime
-109-
System.Media
-110-
System.IO
Contine clasele necesare pentru organizarea unitatilor de memorie si
pentru operatii de intrare/iesire a datelor.Exemplu: citirea sau scrierea
datelor intr-o fila,sau intr-un director.Clasele DirectoryInfo,DriveInfo,
FileInfo si FileSystemInfo,ofera informatii despre unitatile de memorie.
EXEMPLU: using System;
using System.IO;
class TestMemorie {
public static void Main(){
DriveInfo[] unitati = DriveInfo.GetDrives();
foreach(DriveInfo d in unitati){
Console.WriteLine("Unitatea: {0}",d.Name);
Console.WriteLine("Este de tip: {0}",d.DriveType);
if (d.IsReady == true) {
Console.WriteLine("Formatul= {0}", d.DriveFormat);
Console.WriteLine(
"Memoria libera: {0,15} Mb",d.TotalFreeSpace >> 20);
Console.WriteLine(
"Total memorie: {0,15} Mb",d.TotalSize >> 20);
Console.ReadLine();
} } } }
Salvati fila cu numele DriveInfo1.cs si compilati.
Clasa File,controleaza operatiile din interiorul unei file.
EXEMPLU: using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
public class Form1 : Form{
public static void Main() { Application.Run(new Form1()); }
private Button buton1;
private RichTextBox text1;
public Form1() {
this.Size = new Size (800,600);
buton1 = new Button();
buton1.Left = 20;
buton1.Text = "Citeste fila";
buton1.Click += new EventHandler(buton1_Click);
text1 = new RichTextBox();
text1.Location = new Point(30,40);
text1.Size = new Size(700,500);
text1.Font = new Font("Modern",14,FontStyle.Bold);
this.Controls.Add(buton1);
this.Controls.Add(text1); }
private void buton1_Click(object sender,System.EventArgs e){
text1.Clear();
string path = @"C:\\Fructe.txt";
string[] readText = File.ReadAllLines(path);
foreach (string sir in readText)
text1.AppendText(sir + "\n");
}}
Salvati fila cu numele CitesteFila.cs si compilati.
-111-
In exemplul de mai sus,se utilizeaza un obiect de tip File,pentru a
citi datele dintr-o fila de tip text,si un obiect de tip RichTextBox,
pentru a prezenta datele cu un font special.Datele citite pot fi procesate
analizate,filtrate sau reformatate si apoi pot fi salvate intr-o fila
noua.
EXEMPLU: using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
public class Form1 : Form{
public static void Main() { Application.Run(new Form1()); }
private Button buton1;
private RichTextBox text1;
public Form1() {
this.Size = new Size (800,600);
buton1 = new Button();
buton1.Left = 20;
buton1.Text = "Citeste fila";
buton1.Click += new EventHandler(buton1_Click);
text1 = new RichTextBox();
text1.Location = new Point(30,40);
text1.Size = new Size(700,500);
text1.Font = new Font("Modern",14,FontStyle.Bold);
this.Controls.Add(buton1);
this.Controls.Add(text1); }
private void buton1_Click(object sender,System.EventArgs e){
text1.Clear();
string path = @"C:\\Fructe.txt";
string final = "";
string[] readText = File.ReadAllLines(path);
foreach (string sir in readText){
final = "";
string[] arie2 = sir.Split(new Char[] {'!','.',',',':','?'});
foreach (string sir2 in arie2)
final = final + sir2 + "|";
text1.AppendText(final + "\n");
using (StreamWriter sw = File.AppendText(@"C:\\Salvare.txt"))
{ sw.WriteLine(final); }
}}}
Salvati fila cu numele ScrieFila.cs si compilati.Fata de exercitul
precedent,observati ca fiecare rand citit din fila este separat in functie
de semnele de punctuatie,in siruri.Apoi,fiecare semn de punctuatie este
inlocuit prin separatorul "|" (adica textul este reformatat).In final se
construieste un obiect de tip StreamWriter pentru a scrie datele intr-o
fila noua,special creata.
System.IO contine numeroase stream-uri specializate : BinaryReader,
BinaryWriter,BufferedStream,FileStream,MemoryStream,Stream,StreamReader,
StreamWriter,TextReader,TextWriter.Cititi cu atentie proprietatile si
metodele fiecarui stream,inainte de a alege obiectul care se potriveste
cel mai bine cu necestiatile din aplicatie.Oferta este foarte bogata si
extrem de maleabila.
-112-
System.Diagnostics
class Procese{
static void Main(){
Process [] localAll = Process.GetProcesses();
foreach (Process p1 in localAll)
{
Console.Write("Id = {0} ",p1.Id);
Console.WriteLine("Nume = {0}",p1.ProcessName);
}
Console.ReadLine();
}}
Salvati fila cu numele Procese.cs si compilati.
O astfel de aplicatie este asemanatoare cu Task Manager din Windows XP,
si poate fi apelata in orice moment,pentru a identifica ce procese se
afla in executie intr-un anumit moment dat.Identificarea proceselor are
rost doar in cazul mediului de executie multitasking,cu mai multe procese
executate in paralel.Identificarea proceselor este esentiala atunci cand
se supraincarca memoria de operare si atunci cand doriti sa echilibrati
dimensiunea modulelor ce formeza o aplicatie,pentru a creste viteza de
executie.
Clasa Process se poate utiliza si pentru a lansa in executie un anumit
process,sau un anumit modul executabil.
EXEMPLU:
using System;
using System.Diagnostics;
using System.ComponentModel;
class ProcesNou {
start void main() {
Process.Start("Iexplore.exe","www.gsp.ro");
}}
Salvati fila cu numele Proces1.cs si compilati.Exemplul de mai sus
deschide automat fila gsp.ro cu browserul implicit (IExplore.exe).In
mod similar,puteti deschide automat doua sau mai multe module,necesare
pentru a crea mediul de memorie in care se desfasoara aplicatia,sau pur
si simplu pentru a deschide simultan programele preferate.
-113-
O alta activitate de mentenanta pentru sistem,implica cronometrarea
duratei de executie pentru anumite pocese,sau seturi de operatii.Pentru
simplificarea acestei activitati,System.Diagnostics contine un cronometru
simplu,extrem de usor de implementat: se creaza obiectul, se porneste cu
Start,se opreste cu Stop si apoi se evalueaza timpul scurs cu ajutorul
proprietatii Elapsed.
EXEMPLU:
using System;
using System.Diagnostics;
using System.Windows.Forms;
class Test {
static void Main(){
Console.WriteLine("Vezi fila Depanare.txt");
FileStream fs = new FileStream("Depanare.txt",FileMode.Create);
StreamWriter sw = new StreamWriter(fs);
Console.SetOut(sw);
Console.WriteLine("Inceputul inregistrarii:");
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
Trace.AutoFlush = true;
Trace.Indent();
int x;
x= 23 + 7;
x= x + 15;
x= x * 3;
Trace.WriteLine("Valoarea actuala a lui x este: ");
Trace.WriteLine(x);
Trace.Unindent();
sw.Close();
Console.ReadLine()
}}
Salvati fila cu numele Trace1.cs si compilati cu: csc /d:TRACE Trace1.cs
in exemplul de mai sus,datele pentru output sunt dirijate spre o fila
text externa.In acest mod,se poate urmari o anumita valoare in cursul unor
operatii succesive,sau se pot verifica diferiti parametri in momente
succesive ale executiei.O astfel de fila,se poate crea si pentru a arhiva
valorile introduse de utilizator in timpul executiei (pentru back-up).
Datele marcate cu Trace sau Debug pot fi trimise simultan spre mai
multe locatii.Pentru acest scop,se adauga cate un Listener pentru fiecare
dintre locatiile dorite.Exemplu: se pot dirija datele simultan spre o
fila de arhivare,spre Console si spre o fereastra de tip text.Colectia de
auscultatori (Listeneri) este comuna pentru ambele clase,dar datele nu se
pot incrucisa deoarece la compilare se utilizeaza doar una dintre optiuni.
Acest mecanism a fost conceput pentru depanarea codului,dar se poate
aplica pentru diverse alte scopuri.Exemple: se pot arhiva preferintele
fiecarui utilizator,se pot analiza numarul de executii si optiunile alese,
se poate genera un raport de executie...etc.
-115-
Microsoft Visual C# 2008 Express Edition
-116-
Programul poate fi descarcat de la adresa www.microsoft.com.Instalarea
nu ridica probleme deosebite.Este recomandabil sa instalati si referinta
Help MSDN Express Library.Dupa instalare,este recomandabil sa urmariti si
aplicatia video in care este prezentata interfata visuala,afisata in
caseta Getting Started sub numele: Video Feature Tour.Un alt wizard simplu
denumit: Create Your First Application,prezinta primii pasi necesari
pentru a creea o aplicatie simpla.Daca doriti sa verificati cunostiintele
dobandite in capitolele anterioare,este bine sa incepeti cu o aplicatie
de tip Consola.
EXEMPLU:
Din meniul File,alegeti New Project si apoi selectati optiunea Console
Application.Se va deschide automat o fila de tip text,cu extensia .cs,
denumita Program.cs in care este preeditat scheletul aplicatiei format
din namespace ConsoleApplication1,o clasa denumita Program si functia
Main().Pentru a gestiona resursele aplicatiei,puteti utiliza fereastra
denumita: Solution Explorer (la stanga ecranului).In aceasta fereastra
puteti naviga prin intregul mediu de dezvoltare (IDE) si puteti observa
toate filele din proiect.In aceasta etapa nu figureaza decat fila de
start: Program.cs.
Sa presupunem ca doriti sa editati un executabil cu care sa deschideti
automat ziarul preferat (Gazeta Sporturilor).Este necesar sa adaugati in
lista de resurse si: using System.Diagnostics.
Apoi includeti in functia Main() urmatoarea linie de comanda:
Process.Start("Iexplore.exe","www.gsp.ro");
Pentru a compila si previzualiza proiectul apasati tasta F5.
Daca nu exista nici o eroare,proiectul va fi compilat si executat.Acelasi
rezultat se poate obtine si selectand meniul Build si apoi Build Solution.
Daca exista erori,acestea vor fi afisate in fereastra de depanare rapida
denumita: Error List.In caz ca utilizati un alt browser implicit,trebuie
sa specificati denumirea si adresa corecta.
Daca doriti sa schimbati numele aplicatiei,sau numele clasei inlocuiti
identificatorul namespace (Exemplu: ProgramulMeu) si respectiv cel al
clasei (Exemplu: Gazeta Sporturilor).Apoi reconstruiti cu Build sau cu
Rebuild Solution.
In final,salvati programul selectand Build si Publish.
Se va deschide un wizard automat in care apasati butonul Browse si alegeti
adresa de destinatie.Puteti alege modul de arhivare,in functie de
utilizarea finala.Alegeti instalarea de pe CD sau DVD.Aplicatia nu va fi
actualizata in viitor,asa ca puteti utiliza optiunea implicita.
Aplicatia a fost salvata cu numele implicit: ConsoleApplication1.In
continuare,programul va oferi cate un nume implicit pentru fiecare proiect
nou: ConsoleApplication2....etc.Daca doriti sa folositi un nume ales de
d-voastra,utilizati fereastra Solution Explorer pentru a schimba numele
proiectului,sau cel al modulului executabil.
Inainte de a parasi proiectul,este bine sa salvati fila .cs,cu optiunea
SaveAs,la aceeasi adresa la care a fost publicat proiectul.Fila este
salvata si implicit,dar la o adresa mult mai greu de navigat.
In continuare,pentru a putea actualiza sau modifica proiectul,puteti
sa redeschideti fila .cs cu optiunea Open File.
Pentru a beneficia de utilitarul IntelliSense,este suficient sa
amplasati indicatorul mouse pe codul dorit (in fereastra de editare).
-117-
Windows Forms Application
-118-
Platforma Visual este un mediu de dezvoltare foarte comod,dar nu face
minuni.Se utilizeaza exact aceleasi obiecte si tehnici de programare ca
si in cazul compilatorului csc.exe.Diferenta consta in fereastra de
design visual si in cele cateva instrumente ajutatoare,menite doar sa
simplifice organizarea si conceptia programului.Orice program creat cu
Visual C# poate fi creat sau transpus si pentru alte compilatoare de C#.
Exemplu OpenFile
Pentru un prim exemplu,sa presupunem ca doriti sa creati o aplicatie
de tip Windows cu care sa puteti deschide orice fila de tip text,cu un
anumit set de fonturi.Programul va utiliza o fereastra,un buton si un
obiect RichTextBox pentru afisarea textului.Pentru a simplifica cat mai
mult navigarea se va utiliza un obiect de tip OpenFileDialog.Cele trei
obiecte vor functiona in felul urmator:
1. -la apasarea butonului se deschide obiectul OpenFileDialog cu metoda
ShowDialog()
2. -dupa selectarea filei,textul este copiat intr-un strem si apoi
transferat in obiectul de afisare RichTextBox,folosind metoda
LoadFile()
Dupa proiectarea schemei de principiu,se poate trece la implementare.
Din meniul File,alegeti New Project si apoi Windows Forms Application.
Se va deschide automat fila de proiect WindowsFormsApplication1.Observati
fereastra Form1.cs[Design] in care este afisata fereastra Form1 si cele
doua ferestre ajutatoare: Solution Explorer si Properties.In plus,la
dreapta ecranului este afisata o bara de instrumente (Toolbox) in care
sunt afisate toate obiectele visuale de tip Windows Forms.Daca doriti sa
limitati numarul de obiecte afisate,executati un click pe butonul de
langa textul: All Windows Forms si alegeti alt meniu:
Exemplu: Common Controls
Fereastra Form1.cs,este fereastra de design in care se va proiecta
interfata grafica cu utilizatorul.In aceasta fereastra,obiectele visuale
vor fi afisate asa cum vor fi reprezentate si in programul final.
Pentru a schimba numele proiectului,alegeti din meniul Project,
optiunea Properties (ultima) si apoi alegeti numele dorit pentru
Assembley Name: Exemplu: FilaText
Apoi folositi cursorul orizontal si alegeti un nume pentru
Default namespace: Exemplu: FilaText
In aceasi interfata,puteti alege versiunea platformei .Net utilizata
ca sursa pentru resurse,tipul de output,obiectul de start si imaginea
icon folosita pentru executabilul creat,tipul de manifest si resursele
incluse automat.Restul meniurilor din interfata permit setarea altor
proprietati din proiect.
Fereastra Form1 este fereastra Windows utilizata ca suport (container)
pentru restul obiectelor din program.Pentru a schimba aspectul acestei
ferestre,selectati fereastra cu un click si apoi urmariti fereastra
Properties.Pentru a schimba numele ferestrei,alegeti proprietatea Text
si editati noul nume: Afisare text
Fereastra este insa prea mica pentru a cuprinde o pagina de text.
Alegeti proprietatea Size si schimbati valorile cu: 800;600
Daca doriti o alta culoare de fond,puteti utiliza proprietatea
BackColor sau puteti adauga o imagine cu BackgroundImage.
Proprietatea Location poate seta pozitia ferestrei pe ecran.
-119-
Daca doriti sa studiati si codul aflat in spatele ferestrei de design
executati un dublu click pe fereastra.Se va deschide fereastra Form1.cs
in care puteti observa bibliotecile importate automat si scheletul de
cod creat implicit.Proiectul utilizeaza implicit spatiul denumit
WindowsFormsApplication1.Daca doriti un alt identificator pentru namespace
puteti schimba acest nume,dar va trebui sa repetati operatia pentru toate
filele de cod din program.Daca doriti sa vedeti si fila principala din
program,alegeti din fereastra Solution Explorer fila Program.cs.In
aceasta fila se gaseste functia Main() si link-urile spre restul filelor
din program.
Apoi reveniti la fereastra From1.cs[Design] pentru a continua
dezvoltarea interfetei.Alegeti din Toolbox un obiect de tip Button si
trageti cu butonul mouse apasat pana cand obiectul apare in fereastra
Form1.Asezati butonul in pozitia dorita si apoi schimbati textul in
fereastra Properties,alegand proprietatea Text:
Exemplu: Citeste Textul
La nevoie redimensionati obiectul.
In continuare,alegeti din Toolbox si trageti in fereastra un obiect
de tip RichTextBox.Amplasati obiectul imediat sub buton si apoi
schimbati proprietatea Size la valorile: 725;475
Alegeti fontul preferat,cat mai usor de citit:
Exemplu: Microsoft Sans Serif;12pt;style Bold.
Pentru a seta fontul,apasati butonul ... din proprieatatea Font.
Cele trei obiecte,vor forma interfata grafica cu utilizatorul.Daca
nu sunteti multumiti de aspect,utilizati fereastra Properties pentru
a schimba culorile,pozitia,dimensiunile...etc.
Pentru a simplifica functionalitatea,selectati din Toolbox si trageti
in fereastra si un obiect de tip OpenFileDialog.Acest obiect nu este
visual in forma neactivata,asa ca nu va fi reprezentat in fereastra
Form1,dar va aparea in fereastra de design sub forma unei pictograme mici
insotita de numele obiectului: openFileDialog1.
In continuare se poate dezvolta functionalitatea programului.Pentru
a edita metoda Click() a butonului,este suficient sa executati un dublu
click pe buton (sau alegeti butonul Events din fereastra Properties)
Se va afisa fereastra Form1.cs cu scheletul metodei gata editat.
Completati intre acolade urmatoarea expresie:
openFileDialog1.ShowDialog();
Apoi trebuie editata metoda Click pentru butonul Open din obiectul
openFileDialog1 (cel cu care se alege fila text).Pentru acest scop,
selectati obiectul openFileDialog1 si alegeti din fereastra Properties
butonul Events (cel cu un fulger),apoi executati un dublu click pe
evenimentul FileOk.Completati metoda astfel:
Stream myStream = null;
try { if((myStream = OpenFileDialog1.OpenFile()) != null)
{ using (myStream)
{ richTextBox1.LoadFile(openFileDialog1.FileName,
RichTextBoxStreamType.PlainText);
}}}
catch (Exception ex)
{ MessageBox.Show("Nu gasesc fila.Eroare: " +ex.Message); }
Programul este deja complet si functional.
-120-
In aceasta configuratie,obiectul OpenFileDialog va deschide automat
toate filele din directorul selectat.Pentru a simplifica alegerea
filelor de tip text,se poate seta un filtru,cu ajutorul proprietatii
Filter.Pentru acest scop,alegeti obiectul openFileDialog1 din fereastra
de design,utilizati fereastra Properties si editati proprietatea Filter
astfel: (*.txt)|*.txt
Inainte de compilare,verificati fereastra Error List.In mod normal
apare un mesaj de avertizare,prin care se semnaleaza faptul ca tipul
Stream nu este definit in program.Executati un dublu click pe Form1 si
adaugati in lista de biblioteci si System.IO
Acum fereastra Errors este goala si se poate trece la compilarea si
previzualizarea aplicatiei.
Apasati tasta F5.
Daca nu a intervenit nici o eroare,programul este functional.Puteti
naviga pe intregul disc,si puteti deschide orice fila de text,utilizand
fonturile preferate.Este momentul pentru ultimele ajustari.Daca nu
sunteti multumiti de fonturi,sau de incadrarea in pagina,puteti sa
alegeti alte fonturi,sa schimbati dimensiunile ferestrei sau cele ale
casetei RichTextBox.Optional,modificati culorile,adaugati unul sau mai
multe obiecte Label cu explicatiile dorite,sau afisati numele si sigla
d-voastra pentru a semnala autorul programului...etc.
In final,construiti aplicatia cu Build,salvati programul cu Publish si
la final salvati si proiectul,pentru a putea fi dezvoltat sau modernizat
ulterior.Este util sa pastrati toate filele de cod,pentru a putea
reutiliza in viitor,unele secvente de cod,sau chiar module intregi.
De exemplu,aceasta aplicatie va putea fi inclusa integral,intr-un
program mai mare,sub forma unui modul extern.Pur si simplu adaugati in
interfata respectiva un buton,care la metoda Click lanseaza in executie
acest modul ( cu Process.Start()).
Acest gen de aplicatie a fost exemplificat si pentru compilatorul
csc.exe.Daca doriti sa procedati ca un profesionist,puteti analiza putin
cele doua aplicatii,comparativ: -timpul de executie,viteza de executie,
spatiul de memorie ocupat,portabilitatea pe alte platforme,integrarea in
alte programe,depanarea si compilarea,operatiile de design...etc.
In continuare,puteti utiliza aceasta aplicatie pentru a verifica o
parte dintre instrumentele ajutatoare oferite de platforma visuala.
Astfel,daca selectati una dintre filele de cod,de exemplu Form1.cs
observati ca deasupra ferestrei de editare exista si doua casete de tip
ListBox ce permit sa va orientati mai usor in program.Prima dintre ele
ofera un link spre alte ferestre si module din program,iar cea de a doua
ofera un link spre principalele structuri din fila existenta.
Exemplu: pentru a cauta codul pentru metoda Click() pentru butonul
button1 este suficient sa alegeti in cea de a doua caseta optiunea
button1_Click() si codul va fi derulat automat pana la metoda dorita.
Acest navigator automat,poate fi foarte util in cazul filelor foarte mari.
In exemplul de mai sus,am pastrat toate denumirile implicite create
automat.Daca doriti,puteti sa inlocuiti toti identificatorii cu alte nume
mai scurte si mai comode.Viata d-voastra va fi mult mai usoara,dar se va
complica munca oricarui alt programator,obisnuit sa pastreze aceste nume
implicite.Alegerea va apartine si depinde si de destinatia finala a
programului: freeware,shareware,strict privat,comercial...etc.
-121-
Exemplu Album Foto
Atunci cand o aplicatie Windows utilizeaza si file de resurse externe,
exista doua tipuri de solutii pentru implementare:
1.resursele sunt fixe,caz in care pot fi importate si inglobate
permanent in aplicatie
2.resursele sunt variabile,in functie de dorinta utilizatorului si
trebuie importate sau eliberate dinamic prin operatii de tip I/O.
Prima situatie este banala si nu necesita o descriere separata.Pur si
simplu se importa fila de resurse in proiect,fie direct fie intr-un
director special denumit Resources (creat implicit).Pur si simplu se
trage fila cu butonul mouse apasat,spre fereastra Solution Explorer.
Apoi se creaza link-urile interne necesare.
Cea de a doua situatie,este putin mai complexa.Este necesar ca aplicatia
sa tina permanent evidenta resurselor importate.Pentru acest scop,se poate
utiliza un obiect de tip colectie (Lista,Dictionar,Arie,Stiva etc) sau se
poate apela la o fila de arhivare.Solutia clasica include o baza de date,
un tabel preformatat si serverul SQL.Baza de date asigura securitatea
informatiilor,iar serverul asigura un auscultator (listener) si operatiile
de gestiune si control.Totusi,serverul SQL ocupa 800 Mb de memorie,iar
conexiunea la server consuma din capacitatea de lucru a procesorului.In
plus,exista numeroase situatii banale in care nu este necesara securizarea
datelor.In toate aceste situatii,evidenta resurselor din program se poate
organiza cu o simpla fila de tip text.
EXEMPLU: Sa presupunem ca doriti sa realizati un album de familie,
in care fotografiile sa poata fi derulate cu un singur buton,iar clientul
sa poata organiza numarul si ordinea fotografiilor dupa bunul plac.In plus,
trebuie ca aplicatia sa fie portabila si sa functioneze la orice adresa
de memorie.Se va utiliza o fereastra Windows,un buton si un obiect de tip
PictureBox.Pentru a organiza fotografiile,se va utiliza o fila de tip text,
in care utilizatorul va scrie sub forma de lista,numele si extensia pentru
fiecare fila dorita,astfel:
Foto1.jpg
Foto2.jpg
Foto3.jpg
Salvati fila cu numele Poze.txt.
In continuare,programul va deschide fila text si va copia lista de
resurse intr-un obiect de tip arie,de unde vor putea fi preluate rotativ,
pentru a fi afisate in obiectul PictureBox.
Platforma Microsoft Visual C sharp,ridica o mica problema atunci cand
resursele locale trebuie sa fie incarcate dinamic.Directorul implicit al
proiectului,cel in care se proiecteaza etapa de design,este la o adresa
destul de dificil de controlat (in Local Settings).Din acest motiv,este
recomandabil ca inainte de a incepe lucrul,sa salvati proiectul la o
adresa mai accesibila,apoi sa reincarcati proiectul de la aceasta adresa.
Exemplu: salvati pe unitatea C un director nou,denumit Aplicatii.Acest
director va fi utilizat pentru designul proiectului si pentru a putea
organiza resursele locale (cele initiale).
Apoi deschideti un nou proiect de tip Windows Forms Application.
Eventual redenumiti proiectul,apoi alegeti din meniul File optiunea
Close solution si salvati proiectul la adresa :
C:\\Aplicatii
-122-
Apoi,alegeti din meniul File optiunea Open Project si reincarcati
proiectul,de la adresa C:\\Aplicatii.In fereastra Solution Explorer va
apare arborele proiectului.Pentru a continua,selectati Form1.cs.
Pentru a putea continua editarea,este recomandabil sa verificati
care este directorul curent al proiectului in etapa de design.Pentru
acest scop,adaugati un buton si o eticheta Label,apoi executati un
dublu click pe buton.
Completati metoda button1_Click() astfel:
label1.Text = Directory.GetCurrentDirectory();
apoi adaugati in lista de biblioteci si:
using System.IO;
Apasati tasta F5 pentru a compila proiectul,apoi apasati butonul.
In mod normal se va afisa urmatoarea adresa:
C:\Aplicatii\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug
Aceasta este adresa implicita la care trebuiesc adaugate toate
resursele locale ale proiectului,adica fila Poze.txt si fotografiile.
In continuare,proiectul este usor de realizat.Deschideti fereastra
Form1.cs[Design],adaugati un obiect PictureBox si redimensionati fereastra
si pictureBox1 dupa bunul plac. (Exemplu: 800/600 si 750/550).Alegeti
culorile dorite,fontul pentru label,apoi executati un dublu click pe
buton si modificati codul astfel:
public partial class Form1 : Form {
Bitmap bmp;
String [] poze;
public int pozitie,numar;
public Form1() {
InitializeComponent();
pozitie = 0;
string path1 = Directory.GetCurrentDirectory()+"\\Poze.txt";
poze = File.ReadAllLines(path1);
bmp = new Bitmap(Directory.GetCurrentDirectory()+"\\"+poze[0]);
pictureBox1.Image = (Image)bmp;
this.Refresh();
label1.Text = poze[0];
numar = poze.Length; }
private void button1_Click(object sender,EventArgs e) {
if (pozitie < numar - 1) {
pozitie = pozitie + 1;
bmp = new Bitmap(Directory.GetCurrentDirectory()+"\\"+poze[pozitie]);
}
else {
bmp = new Bitmap(Directory.GetCurrentDirectory()+"\\"+poze[0];
pozitie = 0;
}
label1.Text = poze[pozitie];
pictureBox1.Image = (Image) bmp;
this.Refresh(); }
private void Form1_Load(object sender,EventArgs e) { }
}}
Verificati ultimele detalii de design si apoi apasati tasta F5 pentru
compilare si executie.
-123-
Daca nu a intervenit nici o eroare,proiectul integral poate fi
recuperat din directorul Debug de la adresa C:\Aplicatii.....
Pentru a verifica portabilitatea aplicatiei,creati pe Desktop un
director nou,denumit Test,in care copiati toate filele din directorul
Debug,inclusiv fila Poze.txt si filele .jpg.
Pentru a verifica si incarcarea dinamica,schimbati ordinea din lista
editata in fila Foto.txt,adaugati fotografii noi,sau eliminati o parte
dintre ele.
Pentru a dezvolta proiectul se pot adauga butoane suplimentare pentru
navigatie,sau pentru personalizarea interfetei,cursoare pentru a putea
redimensiona fereastra sau imaginea,un buton OpenFile,pentru a putea
deschide si alte imagini decat cele din fila de resurse,un player audio
pentru fundalul sonor,un obiect Textbox in care se afiseaza explicatii
pentru fiecare imagine,link-uri automate spre resurse din retea...etc.
Exista si situatii in care nu doriti ca utilizatorul sa stie cum se
face gestiunea resurselor.In acest caz,puteti sa adaugati si o interfata
visuala cu casete tip TextBox si Liste sau Dictionare in care clientul
alege vizual filele de resurse.Resursele pot fi arhivate intr-o fila
locala cu atributul hidden.In acest caz,depanarea si controlul nu se va
putea face decat de catre administratorul autorizat (cel care are acces
la fila .txt).
Prin acest mecanism simplu,se pot inlocui un numar foarte mare de
operatii de securizare si control,se reduce consumul de memorie si
implicit creste viteza de executie.Acest tip de solutie,nu se poate
utiliza atunci cand datele clientului au caracter secret.Fila tip text nu
ofera un grad de securitate acceptabil si poate fi corupta sau citita
extrem de usor.Societatile comerciale,firmele private sau orice alt gen
de date cu caracter profesional,se vor proteja cu ajutorul serverelor si
al parolelor de acces.
Filele tip text se pot utiliza si pentru solutii mai complexe.In acest
caz,listele vor contine un text formatat,ce poate fi apoi descompus si
analizat cu ajutorul unui procesor de text.
EXEMPLU:
Foto1.jpg|600;400|Text explicativ
Foto2.jpg|400;200|Text explicativ
Foto3.jpg|500;300|Text explicativ
In exemplul de mai sus,formatul textului este dat de separatorul |.
Textul poate fi descompus cu Split() in trei string-uri diferite ce contin
numele resursei,dimensiunea si textul explicativ.Datele astfel descompuse
vor putea fi utilizate,la fel ca si cele dintr-un tabel.
Acest gen de solutii,au mai ales un caracter didactic si dezvolta
tehnicile de programare ale studentilor si incepatorilor,dar pot oferi si
o solutie alternativa pentru tehnologiile standard.
O solutie asemenatoare de gestiune a resurselor locale,include doar o
interfata grafica,in care utilizatorul face selectii si grupeaza setul
de resurse in containere de tip ListBox sau Dictionary.In acest caz,
selectiile vor avea doar valoare de instanta si nu vor putea fi reluate
sau repetate la executiile viitoare.Acest gen de solutie se prefera
atunci cand nu este important sa se arhiveze optiunile clientului (pentru
a elibera memoria de zeci si sute de file tip client in care sunt arhivate
preferintele fiecarui client).
-124-
Exemplu SoundPlayer
-125-
d = null;
fis = null;
numar = null;
GC.Collect();
}
in final editati metoda timer_Tick astfel:
-129-
Exemplu TextToHTML Converter
-130-
Exemplu Web Browser
-131-
Exemplu cu baza de date (Biblioteca)
-132-
In continuare,este bine sa alegeti obiectele pentru interfata grafica.
Din meniul Tools,alegeti Choose Toolbox Items si apoi selectati din
paleta .NET Framework Components si urmatoarele componente:
BindingNavigator,BindingSource,DataGridView,DataSet,DataTable,DataView,
DataViewManager,OdbcDataAdapter,OleDbDataAdapter,Query si TableAdapter.
Dupa ce setul de obiecte utile este complet,se poate trece la etapa
de design.Deschideti un proiect nou si salvati proiectul in C:\Aplicatii,
pentru a putea avea acces la resurse.Apoi redeschideti proiectul si
adaugati in fereastra Form1 un obiect de tip DataGridView.Conexiunea
creata anterior este pur fictiva.Pentru a conecta grila de afisare la
tabel,utilizati fereastra Choose DataSource,sau alegeti din fereastra
Properties,DataSource si utilizati optiunea Add Project Data Source,pentru
a lansa wizard-ul de conectare.Completati wizard-ul.Dupa conectare,va
apare o fereastra in care confirmati daca doriti sa copiati sursa de date
in proiect (in caz contrar puteti sa o adaugati manual).Dupa completarea
wizard-ului,in obiectul dataGridView1 va apare structura tabelului.Daca
doriti sa verificati datele,apasati tasta F5.
Este momentul sa redimensionati fereastra si grila,pentru un aspect
cat mai placut.Daca doriti ca grila sa se adapteze automat in functie de
datele din tabel,selectati dataGriedView1,alegeti din fereastra Properties
AutoSizeColumnsMode si AutoSizeRowsMode si setati valoarea AllCells.
Pentru a facilita navigarea in tabel,puteti adauga si un obiect de tip
BindingNavigator,in coltul de sus al ferestrei.Pentru a conecta obiectul
la tabel,selectati bindingNavigator1,apoi alegeti in fereastra Properties
BindingSource si setati optiunea cartiBindingSource (obiectul este creat
automat in momentul conectarii grilei la tabelul Carti).Tastati F5.
Apasand pe butoanele navigatorului puteti sa va deplasati in tabel,sa
adaugati sau sa eliminati inregistrari.Conexiunea cu baza de date este
insa intrerupta.Toate modificarile se vor face intr-un tampon de memorie
si vor avea efect doar pentru instanta respectiva de program.Pentru a
putea salva noile inregistrari,adaugati un buton denumit Update si
editati evenimentul click al butonului astfel:
this.cartiTableAdapter.Update(this.bibliotecaDataSet);
Pentru a putea adresa si formule SQL,adaugati si un obiect de tip
TextBox,un buton denumit Query,si un obiect de tip OleDbDataAdapter,
apoi editati metoda pentru buton astfel:
this.oleDbDataAdapter1.SelectCommand.Connection.Open();
this.OleDbDataAdapter1.SelectCommand.CommandText = textBox1.Text;
this.oleDbDataAdapter1.SelectCommand.Connection.Close();
this.bibliotecaDataSet.Carti.Rows.Clear();
this.oleDbDataAdapter1.Fill(this.bibliotecaDataSet);
Compilati si executati cu tasta F5.In timpul executiei puteti introduce
in caseta TextBox comenzile SQL dorite:
EXEMPLE: SELECT * FROM Carti WHERE Anul = "1982"
UPDATE Carti Set Colectia = "BPT" WHERE Editura = "Minerva"
sau DELETE FROM Carti WHERE Anul < "1970"
Atentie: toate operatiile de stergere vor fi definitive.
Pentru operatiile foarte frecvente,puteti adauga cateva butoane care
editeaza si executa formula SQL automat.
Daca este necesar,aceeasi interfata poate afisa simultan mai multe
tabele,sau acelasi tabel dar cu formule diferite de selectie.
-133-
Exemplu cu PerformanceCounter
-134-
Windows Presentation Foundation
-135-
Exemple Grafica 2D
-136-
Deoarece obiectele grafice sunt niste obiecte visuale similare cu
restul componentelor din inerfata grafica,se poate face orice fel de
combinatie intre desene,grafice si butoane sau casete interactive.Nu
este obligatoriu ca obiectele grafice sa fie setate in etapa de design.
Este posibil ca un astfel de obiect sa fie creat,setat sau actualizat,
interactiv,in timpul executiei.Cel mai bun exemplu il reprezinta liniile
de tip Polyline ce pot fi definite printr-o functie oarecare.Acest obiect
este ideal pentru reprezentarea grafica a unei functii.
EXEMPLU: sa presupunem ca doriti sa reprezentati grafic un curent
sinusoidal.Din Tools,alegeti Choose Toolbox Items si adaugati in paleta
Toolbox si un obiect de tip Polyline.Deschideti un proiect nou si apoi
adaugati in fereastra Window1 un buton si un obiect Polyline.Selectati
obiectul Polyline si setati proprietatile:
Height = "90" Margin = "10,0,10,20" Stroke = "Red" Fill = "Cyan" si
StrokeThickness = "3"
Apoi executati un dublu click pe buton si editati metoda button1_Click
astfel:
private void button1_Click(object sender,RoutedEventArgs e){
for(int i=0;i<71;i++){
double x = i * Math.PI;
double y = 40 + 30 * Math.Sin(x/10);
polyline1.Points.Add(new Point(x,y));
}}
Compilati si executati cu F5.In mod similar,se poate reprezenta grafic
orice functie matematica,sau se pot realiza efecte de animatie bazate
pe ecuatii si functii matematice.
In exemplele de mai sus,coordonatele grafice au fost cele ale ferestrei
Window1.Pentru efecte de animatie,este preferabil sa utilizati un obiect
de tip Canvas,pentru controlul coordonatelor.
EXEMPLU: Deschideti un proiect nou,si adaugati in fereastra Window1 un
obiect de tip Canvas.Setati proprietatile: Margin = "10,5,5,75" si
Background = "Coral".Adaugati un buton in coltul de sus al ferestrei,apoi
un obiect de tip Ellipse si setati proprietatile: "Canvas.Left = "30",
Canvas.Top = "90" Height = "50" Width = "50" Fill = "Blue" apoi un al
doilea obiect de tip Ellipse si setati proprietatile: Canvas.Left = "180"
Canvas.Top = "30" Height = "50" Width = "50" Fill = "Yellow".
Pentru animatie,executati un dublu click pe buton si editati metoda
button1_Click astfel:
Canvas.SetLeft(ellipse1,150);
Canvas.SetTop(ellipse1,30);
ellipse1.Height = 30;
ellipse1.Width = 80;
Canvas.SetLeft(ellipse2,50);
Canvas.SetTop(ellipse2,100);
ellipse2.Height = 80;
ellipse2.Width = 30;
Compilati si executati cu F5.Observati ca in acest caz,toate obiectele
sunt conectate la panoul Canvas.Daca incercati sa deplasati obiectul
Canvas in fereastra de design,cercurile si butonul se vor deplasa impreuna
cu el.Obiectul Canvas se va utiliza si pentru a schimba coordonatele
grafice ale obiectelor incluse (Canvas este container pentru acestea).
-137-
Exista si situatii in care doriti ca utilizatorul sa poata scrie,sau
sa poata desena ceva,pe o tabla improvizata,in timpul unei conferinte.
Pentru acest scop,este ideal obiectul InkCanvas.
Exemplu: -deschideti un nou proiect.Adaugati din Tools/Choose Toolbox
Items un obiect de tip InkCanvas in paleta Toolbox,apoi trageti obiectul
in fereastra Window1.Setati dimensiunile si culoarea de fond dorita si
apoi compilati cu F5.In timpul executiei,se poate utiliza indicatorul
mouse,pentru a scrie sau desena in aria obiectului.Pentru a putea sterge
tabla (obiectul InkCanvas) adaugati si un buton si apoi editati metoda
button1_Click astfel:
inkCanvas1.Strokes.Clear();
O astfel de fereastra se poate include in interfata grafica prin
intermediul unui buton de activare (pentru economie de spatiu).
Paleta de culori oferita de obiectele grafice este destul de bogata.
Totusi,exista si situatii in care doriti sa combinati doua sau mai multe
culori,pentru a obtine gradiente de culoare,degrade-uri etc.Pentru acest
scop,se pot utiliza cele doua pensule speciale,denumite LinearGradientBrush
si RadialGradientBrush.Cele doua pensule se pot aplica pentru proprietatea
Fill,in locul pensulei implicite SolidBrush.
Exemplu: -deschideti un proiect nou si apoi adaugati in paleta Toolbox
un obiect de tip Rectangle.Trageti obiectul in fereastra Window1 si
setati dimensiunile,culoarea pentru Stroke...etc. Pentru a putea include
in definitia obiectului Rectangle o pensula noua,alegeti fereastra
Window1.xaml si modificati tag-ul pentru Rectangle astfel:
<Rectangle Margin="44,80,50,103" Name="rectangle1" Stroke="Blue">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1.0">
<GradientStop Color="Coral" Offset="0"/>
<GradientStop Color="Yellow" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
Atentie! -pentru a putea include tag-ul Rectangle.Fill in Rectangle,
trebuie sa stergeti linia diagonala de la sfarsitul tag-ului Rectangle.
Pentru a testa si pensula RadialGradientBrush,adaugati si un obiect
de tip Ellipse,apoi includeti in tag-ul Ellipse si urmatoarea pensula:
<Ellipse Height="99" Margin="71,0,70,26" Name="ellipse1" Stroke="Red">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5"
RadiusX="0.7" RadiusY="0.3">
<GradientStop Color="Coral" Offset="0"/>
<GradientStop Color="Yellow" Offset="0.6"/>
<GradientStop Color="LightCoral" Offset="0.9"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
Compilati si executati apasand tasta F5.
Pentru a seta culorile prin cod,se poate utiliza oricare dintre formulele:
color = Color.FromRgb(255,0,0); color = Color.FromArgb(100,255,0,0);
color = Color.FromScRgb(0.5f,1.0f,0.0f,0.0f); color = Colors.Red;
sau color = (Color)ColorConverter.ConvertFromString("#FFFF0000");
-138-
Exemplu Diagrama
-139-
Exemple Grafica 3D
-140-
Camera,si MatrixCamera.Dupa construirea obiectului ViewPort3D,trebuie
setata proprietatea Camera,la una dintre cele trei valori.
PerspectiveCamera este tipul cel mai natural,in care obiectele situate
la distanta mai mare,vor avea dimensiuni mai mici.OrthographicCamera in
schimb,pastreaza distanta dintre punctele retelei si in plan indepartat,
astfel ca obiectele vor avea aceeasi reprezentare in orice locatie a
grilei de afisare (nu vor avea perspectiva).MatrixCamera permite definitia
retelei cu ajutorul a doua matrice de valori: ProjectionMatrix si View-
Matrix,pentru a pemite si efecte de perspectiva angulara.
Pentru a putea crea suprafetele dorite,se utilizeaza un set de obiecte
denumite Model.Clasa abstracta este denumita Model3D,iar clasele drivate
se numesc GeometryModel3D atunci cand definesc o suprafata,sau respectiv
Model3DGroup atunci cand se combina mai multe obiecte de tip Model3D.
Obiectul principal poarta numele de MeshGeometry3D si este de fapt o
retea de triunghiuri ce descriu aproximativ suprafata dorita.Pentru acest
scop,trebuiesc definite punctele retelei,precum si modul in care se
unesc cate trei puncte,pentru a forma un triunghi.Punctele se definesc
prin cate trei valori,cu ajutorul colectiei Positions.Fiecare astfel de
punct este de fapt un obiect de tip Point3D.Relatia prin care se formeaza
fiecare triunghi se defineste prin proprietatea TriangleIndices,tot sub
forma de seturi de cate trei valori.In plus,exista si o proprietate
denumita Normals,in care se seteaza o colectie de valori ce definesc
vectorul prin care se stabileste ce fata a triunghiului este orientata
spre un anumit punct.Aceasta proprietate este esentiala pentru a putea
crea degrade-uri de culoare ce creeaza senzatia de volum.Un astfel de
obiect complet va fi definit astfel:
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="0,1,0 1,-1,0 -1,-1,0"
Normals="0,0,1 0,0,1 0,0,1"
TriangleIndices="0,2,1" />
</GeometryModel3D.Geometry>
</GeometryModel3D>
Definitia de mai sus este pentru un singur triunghi din viitoarea
suprafata.Pentru o suprafata intreaga se vor utiliza seturi mult mai mari
de valori.
Pentru a defini modul in care este prezentata suprafata din interiorul
fiecarui triunghi,se utilizeaza un obiect special,denumit Material.Acest
obiect este asemanator cu pensula din grafica 2D,dar pe langa culoarea
de umplere,permite si diferite efecte optice,cum sunt: DiffuseMaterial,
SpecularMaterial sau EmissiveMaterial.
Sensatia de perspectiva volumica este accentuata si prin crearea de
lumini si umbre,fata de o presupusa sursa de lumina.Obiectul Model,
contine si proprietatea DirectionalLight,prin care se seteaza pozitia
acestei surse de lumina.Cand se face proiectia finala a suprafetei,toate
triunghiurile aflate in apropierea sursei de lumina vor fi colorate cu
nuante deschise ale culorii selectate,in timp ce triunghiurile situate
mai departe de sursa de lumina vor fi umbrite (vor fi colorate cu nuante
din ce in ce mai inchise.
Efectul de ansamblu va fi cel de obiect cu forma aproximativa a celui
real,dar cu volum si perspectiva spatiala.
-141-
EXEMPLU: -deschideti un proiect nou de tip WFP.Completati fereastra
Window1.xaml astfel:
<Grid>
<Viewport3D>
<Viewport3D.Camera>
<PerspectiveCamera Position="-40,40,40" LookDirection="40,-40,-40"
UpDirection="0,0,1"/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3Group>
<DirectionalLight Color="Yellow" Direction="-1,-1,3"/>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions="0,0,0 10,0,0 10,10,0 0,10,0 0,0,10
10,0,10 10,10,10 0,10,10"
TriangleIndices="4 6 7"/>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush="Aqua"/>
</GeometryModel3D.Material>
</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
<Grid>
Compilati si executati cu F5.
Exemplul de mai sus,contine toate definitiile pentru un obiect 3D
complet,dar nu defineste decat un singur triunghi.Doua astfel de
triunghiuri,daca sunt alipite corespunzator pot forma o suprafata plana
paralelipipedica.In exemplul de mai sus,adaugati urmatoarele trei valori
in TriangleIndices: 4 5 6 si observati noua suprafata.
Un cub are sase suprafate,dintre care sunt vizibile in orice moment
doar trei.Pentru a desena un cub,vor fi necesare trei suprafete de tip
paralelipipedic,adica sase triunghiuri,doua cate doua alipite pe o latura.
Pentru o prima impresie,modificati TriangleIndices astfel"
TriangleIndices = " 4 6 7 4 5 6
2 3 7 0 4 3 "
In acest moment,imaginea grafica va contine patru triunghiri,dintre
care primele doua definesc fata de sus a cubului,iar celelalte doua
definesc cate o jumatate din fetele laterale.Observati cum sunt colorate
cele patru triunghiuri in raport cu sursa de lumina.Pentru a completa
cubul,modificati TriangleIndices astfel:
TriangleIndices = " 0 1 3 1 2 3 0 4 3 4 7 3 4 6 7 4 5 6
0 4 1 1 4 5 1 2 6 6 5 1 2 3 7 7 6 2"
Definitia contine si cele trei suprafete care nu se vad.Astfel,daca
inlaturati una dintre suprafetele vizible,vor ramane afisate cele din
fundal.Puteti sa jonglati putin cu seturile de valori si cu cele doua
culori,pentru a obtine nuante de culoare si diverse suprafete,cu forme
neregulate.
-142-
Atunci cand este vorba despre suprafete complexe,formate din foarte
multe triunghiuri,este extrem de nepractic sa se descrie cele trei puncte
ale fiecarui triunghi.In schimb,se pot utiliza functii si algoritmi ce
creaza triunghiurile dupa o anumita regula.
-143-
Daca doriti sa eliberati fereastra inainte de a desena o imagine noua,
va trebui sa eliminati orice model incarcat in ViewPort3D.
EXEMPLU:
ModelVisual3D m;
m = (ModelVisual3D) mainViewPort.Children[0];
mainViewPort.Children.Remove(m);
va sterge primul model din lista de modele incarcate in ViewPort.
Formulele pentru conectarea punctelor astfel incat sa formeze corpuri
geometrice simetrice pot fi gasite in exemplele si tutorialele din reteaua
Internet,sau le puteti concepe d-voastra,in functie de necesitatile din
program.In principiu se lucreza cu formule fixe,ce se adapteaza de la un
program la altul,in functie de relatia cu celelalte obiecte grafice.
Pentru informatii suplimentare si exemple pot fi consultate si
urmatoarele manuale gratuite din reteaua Internet:
"Practical WFP Graphics Programming" autor Ph.D Jack Xu
"Programming WFP O'Reilly" autori Chris Sells si Ian Griffiths
"Windows Presentation Foundation unleashed" autor Adam Nathan
WPF 3D Tutorial la adresa web: http://www.kindohm.com
3-D Graphics Overview la adresa web: http://msdn.microsoft.com
-144-
EXEMPLU: -deschideti din nou proiectul cu primul cub 3D
in fereastra Window1.xaml,adaugati urmatoarea secventa,imediat dupa
GeometryModel3D.Material:
<GeometryModel3D.Transform>
<TranslateTransform3D x:Name="myTranslate"
OffsetX="0" OffsetY="0" OffsetZ="0" />
</GeometryModel3D.Transform>
Pentru a schimba static pozitia cubului,puteti schimba oricare dintre
cele trei valori.Daca doriti sa modificati pozitia in timpul executiei,
adaugati un buton si editati metoda button1_Click astfel:
myTranslate.OffsetX = 5;
myTranslate.OffsetY = 5;
myTranslate.OffsetZ = 5;
Daca doriti ca utilizatorul sa poata interactiona cu graficul,puteti
adauga trei casete TextBox si apoi editati metoda button1_Click astfel:
myTranslate.OffsetX = Double.Parse(textBox1.Text);
myTranslate.OffsetY = Double.Parse(textBox2.Text);
myTranslate.OffsetZ = Double.Parse(textBox3.Text);
Un obiect asemanator este si ScaleTransform3D,ce permite micsorarea
sau marirea la scala a obiectului cu ajutorul proprietatilor ScaleX,ScaleY
si ScaleZ.Si in acest caz,cele trei proprietati pot fi setate independent
una de alta.
EXEMPLU: in exemplul de mai sus,inlocuiti obiectul TranslateTransform3D
prin:
<ScaleTransform3D x:Name="myScale" ScaleX="1" ScaleY="1" ScaleZ="1"/>
apoi modificati metoda Button1_Click astfel:
myScale.ScaleX = 2;
myScale.ScaleY = 0.7;
myScale.ScaleZ = 0.4;
Obiectele 3D pot fi si rotate in intregime,pentru a expune fetele
invizibile,sau pentru diferite efecte speciale.Rotatia se obtine cu
ajutorul unui obiect RotateTransform3D in care se defineste Rotation3D
prin proprietatile Axis si Angle.
EXEMPLU: in exemplul de mai sus,inlocuiti ScaleTransform3D prin:
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation x:Name="myAngleRotation" Axis="0,3,0" Anlge="0"/>
</RotateTransform3D.Rotation>
</RotateTransform>
apoi modificati metoda button1_Click astfel:
myAngleRotation.Angle = 45
Operatiile de transformare a unui obiect 3D sunt foarte usor de
comandat,dar sunt ceva mai greu de executat.Pentru fiecare astfel de
transformare,procesorul executa mii de operatii.Nu este bine sa abuzati
de astfel de transformari,doar pentru a crea mici artificii vizuale.De
cele mai multe ori,datele respective pot fi reprezentate grafic si sub
forma 2D,sau prin animatie 2D cu un efort mult mai mic pentru procesor.
Acest gen de operatii sunt insa indispensabile in proiectare,sau in
aplicatiile bazate pe realitate virtuala,si in proiectia unor grafice cu
determinare multifactoriala.Exemplu: pentru a putea reprezenta dinamic,
un proces care evolueaza cu mai multi factori variabili simultani.
-145-
Exemple Animatie
-146-
Folosind o parte dintre proprietatile obiectului DoubleAnimation se
simplifica foarte mult codificarea operatiilor.Astfel Duration seteaza
durata unui ciclu,From si To seteaza limitele intre care are loc executia
iar AutoReverse permite reluarea intregului set de operatii in sens invers
fata de cel initial.Cu alte cuvinte,obiectul DoubleAnimation poate seta
intr-o singura linie de cod toate operatiile necesare pentru a anima una
dintre proprietatile obiectului.Pentru a intelege mai bine valoarea
acestui obiect,incercati sa creati acelasi efect utilizant una dintre
metodele clasice de animatie (cu un timer).
Obiectul StoryBoard nu este un simplu container ci este un fel de panou
central de comanda ce permite organizarea si controlul asupra unui grup
oarecare de obiecte tip DoubleAnimation.Acest obiect contine la randul sau
un set important de proprietati si metode ce permit controlul tuturor
evenimentelor grafice.Pentru a obtine acelasi rezultat prin mijloacele
clasice sunt necesare seturi intregi de obiecte de comanda si control,sau
nenumarate bucle de repetitie cu executie conditionala.
In exemplul de mai sus,obiectul grafic se modifica in permanenta prin
schimbarea proprietatii sale intre From si To.Un alt gen de animatie se
obtine atunci cand obiectul grafic ramane nemodificat,dar este alternat
cu un alt obiect grafic,sau este deplasat fata de alte elemente grafice
din program.Atunci cand obiectul grafic ramane nemodificat,stilul de
animatie poarta numele de animatie prin cadre (tehnica cinematografica),
sau KeyFrame animation.
EXEMPLU: deschideti un proiect nou si adaugati in Window1.xaml codul:
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard><Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
Duration="0:0:2" RepeatBehavior="Forever">
<ObjectAnimationUsingFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0.0.0">
<DiscreteObjectKeyFrame.Value><LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="Yellow" Offest="0.3"/>
<GradientStop Color="Red" Offset="1.0"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush></DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1">
<DiscreteObjectKayFrame.Value><LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="Yellow" Offset="0.8"/>
<GradientStop Color="Red" Offset="1.0"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush></DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard></BeginStoryboard>
</EventTrigger></Window.Triggers>
Compilati si executati cu F5.
-147-
In acest exemplu,in loc sa alternam proprietatea Offset intre cele
doua valori dorite,am creat doua cadre (imagini) diferite si apoi am
utilizat timer-ul automat pentru a alterna cele doua cadre intre ele.
Fiecare cadru (obiect DiscreteObjectKeyFrame) seteaza proprietatea
KeyTime,pentru a specifica momentul de executie in care va fi incarcat,
iar timer-ul din obiectul Storyboard seteaza prin Duration durata unui
ciclu.Pentru a introduce cadre noi se va incrementa Duration si apoi
se va seta KeyTime pentru fiecare obiect nou.
Acest gen de solutie este de preferat atunci cand de la un cadru la
altul exista un numar mare de modificari in imaginea grafica.Folosind
cadre diferite,toate aceste modificari pot fi executate intr-o singura
operatie.
Exemplu: -pentru a reprezenta o silueta in mers,se pot modifica de la
un cadru la altul pozitia pentru cele patru membre,pozitia capului si
pozitia trunchiului (pentru a crea senzatia de elasticitate).Acelasi
efect poate fi obtinut si modificand pozitia fiecarui membru in parte,dar
cu un numar mult mai mare de operatii procesate/miscare.
O alta solutie pentru animatie o ofera obiectele de tip PathGeometry.
Un astfel de obiect este format la randul sau din obiecte PathSegment si
creaza un traseu,cu aspectul determinat de suma segmentelor componente.
Un obiect grafic poate fi animat astfel incat sa urmareasca acest traseu.
EXEMPLU: -deschideti un proiect nou si adaugati in Window1.xaml codul:
<Canvas Width="250" Height="250">
<Button MinWidth="100" Content="Butonul meu">
<Button.RenderTransform>
<MatrixTransform x:Name="ButtonMatrixTransform">
<MatrixTransform.Matrix>
<Matrix />
</MatrixTransform.Matrix>
</MatrixTransform>
</Button.RenderTransform>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Loaded">
<BeginStoryboard> <Storyboard>
<MatrixAnimationUsingPath
Storyboard.TargetName="ButtonMatrixTransform"
Storyboard.TargetProperty="Matrix"
DoesRotateWithTangent="True"
Duration="0:0:5" RepeatBehavior="Forever">
<MatrixAnimationUsingPath.PathGeometry><PathGeometry
Figures="M 10,100 C 35,0 135,0 160,100 180,190 235,200 210,100"/>
</MatrixAnimationUsingPath.PathGeometry>
</MatrixAnimationUsingPath>
</Stroryboard></BeginStoryboard>
</EventTrigger></Button.Triggers>
</Button></Canvas>
Compilati si executati cu F5. Pentru a anima obiectul se utilizeaza
proprietatea Matrix si un traseu predefinit specificat prin PathGeometry.
Pentru a schimba traseul,este suficient sa modificati valorile din
proprietatea Figures,adica segmentele ce formeaza traseul (vezi si
obiectul PathGeometry).
-148-
Aceleasi solutii tehnice se pot aplica si pentru grafica 3D.De exemplu,
pentru a putea anima cubul din exemplul anterior (cel translatat static
prin TranslateTransform3D x:Name="myTranslate") este suficient sa adaugati
in Viewport3D urmatoarea secventa:
<Viewport3D.Triggers>
<EventTrigger RoutedEvent="Viewport3D.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="myTranslate"
Storyboard.TargetProperty="OffsetX"
From="0" To="16" Duration="0:0:4"
AutoReverse="True" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Viewport3D.Triggers>
Inserati codul chiar inainte de tag-ul </Viewport3D> si apoi compilati
cu tasta F5.
-149-
Exemplu Grid
-150-
Exemplu Data Binding
-151-
In numeroase situatii,optiunile din casetele de dialog sunt destul de
numeroase.Mai mult decat atat,aceleasi optiuni sunt utilizate in doua
sau mai multe programe diferite.Pentru a simplifica munca programatorului
este comod ca aceste optiuni sa poata fi extrase dintr-o resursa externa.
Cel mai usor se poate utiliza o fila de tip XML,ce poate fi editata
extern si asigura un nivel rezonabil de securitate a formatului.Pentru
un exemplu simplu,sa presupunem ca doriti sa incarcati intr-o caseta
de tip ListBox un set de culori.
EXEMPLU: -deschideti un proiect nou de tip WPF
-adaugati o caseta de tip ListBox
-compilati si salvati proiectul la o adresa locala
-editati cu Notepad urmatoarea fila:
<?xml version="1.0" encoding="utf-8"?>
<colors>
<color name="pink"/>
<color name="white"/>
<color name="black"/>
<color name="cyan"/>
<color name="gray"/>
<color name="magenta"/>
</colors>
Salvati fila cu numele Colors.xml
-redeschideti proiectul creat mai sus
-din meniul Project alegeti Add New Item si adaugati o
resursa noua denumita Colors.xml
-inlocuiti fila creata automat cu fila xml editata mai
sus (in directorul proiectului salvat)
-completati codul din Window1.xaml astfel:
<StackPanel>
<StackPanel.Resources>
<XmlDataProvider x:Key="Colors" Source="Colors.xml" XPath="/colors"/>
</StackPanel.Resources>
<ListBox x:Name="setCulori" Height="100" Margin="30,70,30,0"
VerticalAlignment="Top" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource Colors},
XPath=color/@name}"/>
</StackPanel>
Salvati si compilati cu tasta F5.
Se observa ca legatura cu fila sursa se face prin XmlDataProvider.Se
pot utiliza si alte tipuri de resurse externe,cum ar fi filele de tip
text,sau tabelele si bazele de date,dar fila XML ofera datele intr-un
format direct compatibil cu fila xaml.
Prin acest mecanism simplu,resursele pot fi organizate sub forma de
file xml ce pot fi actualizate in afara proiectului,sau pot fi utilizate
concomitent de doua sau mai multe aplicatii.Nu numai ca se simplifica
mult munca programaturului,dar se face si o economie semnificativa de
memorie.Pentru a personaliza gradul de informatii primite de fiecare
utilizator,este suficient sa fie actualizate doar filele xml (resursele).
In continuare,doua sau mai multe obiecte din interfata pot fi cuplate
intre ele,pentru a filtra si/sau supraselecta datele extrase.
-152-
Exemplu cu baza de date
Filele text sau Xml nu sunt practice atunci cand se lucreaza cu seturi
mari de date de acelasi fel.Majoritatea acestor date se structureaza sub
forma de tabele si baze de date.In plus,bazele de date ofera si un grad
crescut de securitate,deoarece accesul la date este restrictionat prin
parola de acces.WPF nu ofera o grila specializata pentru prezentarea
datelor de tip tabel,dar astfel de obiecte pot fi procurate de la diversi
producatori de componente.Grila implicita din interfata WPF nu poate fi
conectata direct la un tabel si nu are o proprietate DataSource similara
cu cea din GridView pentru Window Forms.
Totusi,orice component visual din interfata poate fi conectat la o
baza de date,pentru a valorifica datele dintr-un tabel,la fel ca si in
Window Forms.Este necesar un obiect pentru conexiune de tip OleDbConnector
un OleDbDataAdaptor si respectiv un obiect de tip DataSet si un obiect de
tip DataTable.Sa presupunem ca aveti o baza de date Biblioteca.mdb ce
contine un tabel denumit Carti,cu un anumit numar de coloane si cateva
inregistrari.Sa presupunem ca doriti sa preluati intr-un obiect de tip
ListBox,una dintre coloane (prima).
EXEMPLU: -salvati baza de date la adresa C:\bin\Biblioteca.mdb
-deschideti un proiect nou WPF
-din meniul Tools alegeti Connect to Database si faceti
conexiunea cu baza de date locala
-adaugati in proiect un buton,o caseta TextBox si una ListBox
-adaugati System.Data si System.Data.OleDb in lista using...
-editati metoda button1_click astfel:
string con1 = @"Provider=Microsoft.Jet.OLEDB.4.0;
Data Source = c:\bin\Biblioteca.mdb";
string query = "SELECT * FROM Carti";
OleDbConnection conector = new OleDbConnection(con1);
conector.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter();
adapter.SelectCommand = new OleDbCommand(query,conector);
DataSet s1 = new BibliotecaDataSet();
DataTable table = s1.Tables["Carti"];
adapter.Fill(table);
DataColumn dc1 = table.Columns[0];
DataRowCollection rc1 = table.Rows;
textBox1.Text = dc1.ToString();
foreach (DataRow r1 in rc1)
listBox1.Items.Add(r1.ItemArray.GetValue(0));
conector.Close()
Compilati si executati cu tasta F5.
In exemplul de mai sus se preiau date dintr-o baza de date arhivata la
o adresa locala,fara parola de acces,si fara parola pentru server.Daca
utilizati un server de tip SQL,conexiunea si adaptorul se vor face cu
obiecte de tip SqlConnector si SqlAdapter.In mod similar se poate proceda
si pentru actualizarea sau stergerea datelor din tabel.Fiecare astfel de
operatie va trebui sa redeschida si sa inchida conexiunea printr-o metoda
asemanatoare cu cea de mai sus (utilizati butoane separate).Comanda pentru
selectie permite filtrarea datelor nerelevante.
-153-
Exemplu pentru documentele XPS
-154-
EXEMPLU: -deschideti un proiect nou de tip WPF
-adaugati in fereastra Window1.xaml urmatorul cod:
<Grid Name="Grid1">
<FixedPage Width="21.0cm" Height="30.0cm" x:Name="design1"
Background="White">
<StackPanel>
<TextBlock Foreground="Coral" FontSize="16" Text="Titlul documentului"/>
<TextBlock FontSize="12" Foreground="Blue">
Primul paragraf... </TextBlock>
<TextBlock Background="Aquamarine" FontSize="14" Foreground="Red">
Al doilea paragraf.... </TextBlock>
</StackPanel>
</FixedPage>
<DocumentViewer Name="doc1" Margin="0,56,0,0">
<FixedDocument x:Name="implicit1">
</FixedDocument>
</DocumentViewer>
<Button HorizontalAlignment="Right" Margin="0,12,12,0" Name="button1"
Width="86" Click="button1_Click" Height="30"> OK </Button>
</Grid>
In etapa de design,aspectul documentului se va afisa in Grid.Pentru
a definitiva detaliile de design,micsorati obiectul DocumentViewer sau
adaugati obiectul doar dupa ce pagina este in formatul final.Pentru
a seta culorile,fonturile,dimensiunile,pozitia se poate utiliza si
fereastra Properties.Pentru a putea include pagina fixa in DocumentViewer
executati un dublu click pe buton si editati metoda button1_click astfel:
Grid1.Children.Remove(design1);
PageContent con1 = new PageContent();
((IAddChuld)con1).AddChild(design1);
implicit1.Pages.Add(con1);
adaugati in lista de resurse si: using System.Windows.Markup;
Compilati si executati cu tasta F5.
Observati ca pentru a putea include pagina fixa in obiectul de afisare
sunt necesare urmatoarele etape:
1.-pagina fixa este eliberata din containerul Grid
2.-se creaza un obiect nou de tip PageContent
3.-se introduce pagina fixa in obiectul PageContent
4.-se introduce PageContent in FixedDocument din DocumentViewer
In continuare puteti sa dezvoltati acest exemplu,pentru a forma o
aplicatie completa.Astfel se pot crea un numar mai mare de pagini fixe,
ce pot fi incarcate succesiv,fie cu un singur buton si un algoritm simplu,
fie cu cate un buton dedicat pentru fiecare pagina.
Paginile incluse in DocumentViewer vor fi afisate intr-o maniera
read-only,adica nu pot fi modificate activ.Se pot utiliza instrumentele
oferite de obiect,pentru a imprima textul,sau pentru a schimba stilul
de prezentare.
Atunci cand utilizati frecvent acelasi format si acelasi set de date,
puteti salva continutul paginilor fixe sub forma de resurse externe de
tip pagina XML.Aceste sabloane,vor putea fi apoi importate in orice alta
aplicatie.Orice actualizare sau transformare a sablonului trebuie facuta
inainte de a fi importat in obiectul DocumentViewer.
-155-
Documentele dinamice se adapteaza atat dupa preferintele utilizatorului
cat si in functie de contextul grafic de dispozitiv.In plus,documentele
dinamice ofera facilitati pentru paginatie si incadrarea in pagina.Intr-un
obiect de tip Flow Document castele de dialog si componentele visuale pot
fi active,sau pot fi reprezentate doar grafic,in functie de containerul
utilizat pentru afisare.Astfel,documentul poate fi inclus intr-un obiect
de tip RichTextBox,caz in care pagina va fi reprezentata doar grafic,sau
poate fi inclus in obiecte FlowDocumentReader,FlowDocumentPageViewer si
FlowDocumentScrollViewer,caz in care componentele vizuale vor fi active.
EXEMPLU: -deschideti un nou proiect WPF
-editati fereastra Window1.xaml astfel:
<FlowDocumentReader Height="380" Background="Transparent">
<FlowDocument ColumnWidth="400">
<Section Background="Beige">
<Paragraph Name="paragraf1" Background="Yellow">
Textul initial
</Paragraph>
<BlockUIContainer>
<Button Click="Button_Click">Click aici </Button>
</BlockContainer>
<Paragraph Name="paragraf2" Background="Yellow">
Cel de al doilea paragraf
</Paragraph>
<BlockUIContainer>
<StackPanel>
<Label Foreground="Blue"> Alegeti fructul dorit: </Label>
<ComboBox>
<ComboBoxItem IsSelected="True">mere</ComboBoxItem>
<ComboBoxItem>struguri</ComboBoxItem>
<ComboBoxItem>cirese</ComboBoxItem>
</ComboBox>
<Label Foreground="Red">Alegeti varianta dorita:</Label>
<StackPanel>
<RadioButton> inghetata </RadioButton>
<RadioButton> compot </RadioButton>
<RadioButton> tort </RadioButton>
</StackPanel>
<Label>Caseta Textbox pentru dialog: </Label>
<TextBox Name="text1">
Textul implicit din caseta TextBox
</TextBox>
</StackPanel>
</BlockUIContainer>
</Section>
</FlowDocument>
</FlowDocumentReader>
Observati ca documentul este format din seturi de obiecte de tip
container ce organizeaza obiectele visuale.Pentru a grupa mai multe
componente se poate utiliza un obiect de tip StackPanel,iar pentru a
grupa mai multe blocuri de text se poate utiliza un obiect de tip
Paragraph. FlowDocumentReader ofera si un utilitar de tip Find.
-156-
Pentru a actualiza datele afisate activ,se poate utiliza oricare dintre
evenimentele componentelor visuale din document.De exemplu,pentru a
adauga cateva retusuri prin apasarea butonului,executati un dublu click
pe buton si completati metoda Button_Click astfel:
text1.Text = "Butonul a fost apasat !";
paragraf1.Foreground = Brushes.AliceBlue;
paragraf1.Background = Brushes.Red;
paragraf1.FontSize = 20;
paragraf2.Background = Brushes.Sienna;
paragraf2.Foreground = Brushes.Snow;
Compilati si executati cu tasta F5.
Daca doriti sa cautati un cuvant din document,executati un click pe lupa
si introduceti cuvantul dorit (Exemplu: Textul).
In plus,puteti utiliza butoanele de stil pentru a alege unul dintre
modurile de afisare: PageMode, Two Page Mode sau Scroll Mode, sau puteti
schimba dimensiunea cu Decrease Zoom si Increase Zoom.
Obiectul FlowDocumentReader nu are un buton implicit pentru Print.
Daca doriti sa imprimati documentul afisat,sau sa creati un document de
tip XPS pentru arhivare,trebuie sa apelati explicit metoda Print().
In exemplul de mai sus,adaugati la obiectul FlowDocumentReader si o
proprietate Name="Flow1", apoi adaugati un buton in ultimul StackPanel
si editati metoda butonului astfel:
Flow1.Print();
Prin acest procedeu,se pot crea chestionare cu raspunsuri multiple,
sau formulare cu casete de dialog,in care clientul completeaza casetele
sau alege optiunile dorite,apoi apasa butonul de imprimare pentru a
crea documentul final.Daca este o actiune on-line,documentul poate fi
expediat automat la o anumita adresa,sau poate fi arhivat in server
pentru a seta optiunile unui anumit utilizator...etc.
Numarul mare de obiecte visuale,containere si obiecte ajutatoare ce
pot fi utilizate pentru a crea un document,asigura o flexibilitate foarte
mare,cu nenumarate proprietati si metode,ce permit un numar infinit de
solutii tehnice sau de design.Nu se pot formula recomandari general
valabile.Fiecare programator,va alege si va configura obiectele in functie
de necesitati,dar si in functie de experienta anterioara.
Pentru incepatori,este recomandabil sa urmeze solutii standardizate.
Exista numeroase exemple si sabloane ce pot fi descarcate din reteaua
Internet.Mai mult decat atat,exista programe automate,ce pot genera
orice tip de document,utilizand sute sau chiar mii de sabloane standard.
Nu este o rusine sa utilizati astfel de programe.Munca d-voastra va fi
mult mai placuta si mai usoara.La un astfel de document generat automat
nu va mai trebui decat sa aduceti ultimele mici retusuri,sa alegeti
culorile si nuantele,dimensiunile sau textul implicit.Un astfel de
program este recomandabil mai ales atunci cand documentul este de fapt
o interfata grafica cu evenimente si interactiuni complexe,greu de
organizat.Daca preferati sa programati manual toate evenimentele,trebuie
sa alegeti cu atentie ordinea de prioritate a thread-urilor si gestionati
modul de eliberare a memoriei.O eroare frecventa este atunci cand doua
sau mai multe metode suprascriu aceleasi date,sau atunci cand clientul
introduce date cu un format incompatibil.Pentru a evita aceste erori,se
pot adauga filtre pentru selectia datelor introduse de la tastatura.
-157-
WPF BROWSER XAML APPLICATIONS
-158-
-Deschideti un proiect nou si alegeti WPFBrowser Application
-editati fila Page1.xaml astfel:
<Grid>
<Label Height="24" HorizontalAlignment="Left" Margin="6,6,0,0"
Name="label1"
VerticalAlignment="Top" Width="90">Axa mica</Label>
<Label Height="24" HorizontalAlignment="Left" Margin="8,38,0,0"
Name="label2"
VerticalAlignment="Top" Width="90">Axa mare</Label>
<Label Height="29" HorizontalAlignment="Left" Margin="10,68,0,0"
Name="label3"
VerticalAlignment="Top" Width="90">Culoarea:</Label>
<ListBox x:Name="Culoare" Height="60" Margin="103,62,104,0"
VerticalAlignment="Top" >
<ListBoxItem IsSelected="True">Red</ListBoxItem>
<ListBoxItem>Aqua</ListBoxItem>
<ListBoxItem>Yellow</ListBoxItem>
<ListBoxItem>Green</ListBoxItem>
</ListBox>
<Slider Height="22" Margin="107,6,93,0" x:Name="slider1"
VerticalAlignment="Top" Background="Turquoise" Interval="40"
Minimum="20" Maximum="300" Value="100" />
<Slider Height="22" Margin="106,34,94,0" x:Name="slider2"
VerticalAlignment="Top" Maximum="600" Minimum="20"
Interval="40" Background="Turquoise" Value="200" />
<Ellipse Margin="53,0,53,23" Name="ellipse1" Stroke="Black"
VerticalAlignment="Bottom" >
<Ellipse.Fill>
<Binding ElementName="Culoare" Path="SelectedItem.Content" />
</Ellipse.Fill>
<Ellipse.Height>
<Binding ElementName="slider1" Path="Value"/>
</Ellipse.Height>
<Ellipse.Width>
<Binding ElementName="slider2" Path="Value"/>
</Ellipse.Width>
</Ellipse>
</Grid>
Compilati se executati cu tasta F5.Observati ca proiectul se deschide
direct in browser,astfel ca puteti sa beneficiati de toate utilitarele
din bara de instrumente.De exemplu,daca doriti sa imprimati continutul
filei,alegeti optiunea Print din meniul File.In obiectul ListBox alegeti
culoarea de umplere a elipsei,iar cu cele doua glisoare puteti modifica
cele doua axe ale elipsei.
Exemplul de mai sus,nu utilizeaza link-uri sau resurse externe asa
ca este perfect portabil,indiferent de browser-ul implict.In mod similar
se poate realiza un grafic complex,cu grafica 2D sau 3D si cu seturi
complete de explicatii,ce se adapteaza interactiv la preferintele fiecarui
client.Salvati fila la o adresa locala (cu Close solution).Daca doriti
sa exportati aplicatia,sau doriti sa includeti graficul intr-un proiect
mai complex,trebuie sa atasati si filele anexe (.exe si .manifest).
-159-
Aplicatii cu ferestre multiple
-160-
Concluzii generale,recomandari
***** S F A R S I T ******