Sunteți pe pagina 1din 175

*********************

* Limbajul C sharp *
* ABC-doar *
*********************

*********************************
* autor RUSU MIRCEA AUREL VALER *
*********************************

*************************************
* MOTTO: *
* *
* "Festina lente" *
* *
* "Dans le doute,abtiens-toi" *
* *
*************************************

De ce C sharp:

Pentru ca este cel mai modern limbaj de programare.


C sharp exploateaza baza de date arhivata in platforma .Net,
oferind cele mai complexe solutii pentru orice problema de
programare.Modulele executabile sunt compilate in cod
intermediar si sunt portabile in orice mediu de programare.
C O N T I N U T

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

Limbajul C# (C sharp) este un Standard International,dezvoltat de


firma Microsoft si patentat impreuna cu firmele Intel si Hewlett-Packard,
cu scopul de a oferii programatorilor un limbaj de programare modern,
multi-functional,orientat spre obiect,capabil sa exploateze noile tipuri
de resurse software oferite de platforma .Net.
Principalii dezvoltatori ai acestui limbaj sunt Anders Hejlsberg,
Scott Wiltamuth si Peter Golde,iar standardul de limbaj este reglementat
prin documentul numit ECMA-334/June 2006.
Limbajul nu este o inventie pura,ci este construit clasic,cu aceleasi
principii de baza ca si limbajul C++,sau Java.Majoritatea solutiilor
tehnice,au fost create prin extinderea celor din limbajele C++ si Java,
cu scopul de a simplifica munca programatorilor obisnuiti cu aceste doua
limbaje.Cunoasterea acestor limbaje nu este obligatorie,dar este utila.
Limbajul C# poate fi utilizat eventaul si pentru a invata de la zero
alfabetul programarii,dar acest manual se adreseaza programatorilor deja
avansati si presupune ca stiti deja sa lucrati cu clase si obiecte,sau cu
o interfata vizuala,ca stiti sa comunicati cu sistemul de operare si sa
gestionati file si fisiere,resurse si structuri de memorie.Cu alte cuvinte
acest "abecedar" nu este axat pe problemele fundamentale de programare,
ci doar pe solutiile oferite de limbajul C sharp.Vor fi prezentate si
notiunile fundamentale de limbaj,doar pentru a prezenta particularitatile
limbajului C#,sau pentru a propune solutii simple.
Specialistii,au caracterizat limbajul C# cu urmatoarele calitati:
multiparadigma: imperativa,functionala,generica si orientata spre obiect
sau component.
Prin paradigma se intelege un anumit stil de programare sau un anumit
tip de solutie tehnica,pentru o problema software oarecare.Principalele
paradigme de programare sunt:
1.Programarea imperativa -se face prin procesarea unor instructiuni,prin
care se schimba o anumita stare a sistemului.Programul este o lista
de astfel de comenzi si instructiuni.
2.Programarea functionala -se face prin evaluarea unor functii matematice
si evita structurile de date fixe sau variabile.Intregul program
este controlat cu ajutorul functiilor (Exemplu: functia Main).
3.Programarea procedurala sau structurata - se face prin fragmentarea
operatiilor necesare in seturi.Pentru a obtine o anumita stare a
sistemului,trebuiesc urmate mai multe astfel de seturi de operatii.
Cu alte cuvinte este un tip de programare modulara.
4.Programarea in flux -numita si programare dictata de evenimente,se face
prin raspuns la o anumita schimbare de stare a sistemului,denumita
eveniment (click de mouse,apasarea unei taste...etc.).In mod curent
legatura dintre eveniment si functiile programului se face fie
prin mesaje simple,fie prin thread-uri (linii de executie).
5.Programarea orientata spre obiect -utilizeaza structuri complexe de date
denumite obiecte,pentru a izola seturi de date in fragmente mai
mici de memorie,denumite si namespaces.Interactiunea cu restul
datelor din program se face prin functii specializate,denumite
metode.
6.Programarea declarativa -prezinta logica unui process fara a descrie
insa si fluxul de executie (Exemplu: clasele abstracte).

-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

Pentru a incepe sa invatati limbajul C# sunt necesare urmatoarele


etape:
1.Descarcati din reteaua Internet o versiune de platforma .Net,preferabil
3.5 sau mai noua si instalati suportul SDK in calculatorul d-voasta(daca
nu este deja instalat).
2.Optional,descarcati din reteaua Internet si documentele fundamentale
pentru standardul de limbaj: ECMA-334 si ECMA 335,eventual si ECMA TR/89.
3.Optional puteti descarca tutoriale,carti si articole de specialitate.
4.Daca doriti sa lucrati exclusiv intr-o platforma vizuala,descarcati si
instalati Visual C# 2008 (sau o versiune mai recenta).
Documentatia tehnica pentru platforma .Net nu se gaseste sub forma de
fila independenta si trebuie accesata in reteaua Internet (MSDN Library).
Dupa instalare,suportul (Framework) se va regasi in directorul:
C:\Windows\Microsoft.Net\Framework
cu un nume oarecare: v3.5 sau v3.0 sau v2.0.50727 etc.
Daca doriti sa simplificati accesul,redenumti directorul in v3 si
apoi copiati continutul direct pe unitatea C.In interiorul acestui
director se gaseste fila csc.exe in care este inclus compilatorul C#.
Pentru a va familiariza putin cu sistemul de lucru,puteti incepe sa
construiti primele module executabile.Codul sursa se poate edita cu
orice editor de text (de exemplu Notepad) si se salveaa in file cu
extensia .cs (practic se poate utiliza orice extensie,dar extensia cs
va fi inteleasa de orice alt programator).
EXEMPLU: deschideti Notepad si editati urmatorul cod:
using System
class WelcomeCSS
{
static void Main()
{
System.Diagnostics.Process.Start("notepad.exe");
}
}
Salvati fila cu numele StartNotepad.cs.
Pentru a compila fila intr-o asamblare (un executabil) deschideti din
Start utilitarul CommandPrompt si navigati la adresa la care este arhivat
directorul ce contine compilatorul.
Exemplu: CD C:\v3
apoi introduceti comanda de compilare:
csc.exe StartNotepad.cs
Daca nu exista si o versiune valida de Visual C# vor apare eventual niste
mesaje de avertizare de tip (warning CS1668).Puteti neglija aceste
mesaje (sau instalati si Visual C#).
In mod normal,in directorul curent a aparut un executabil Windows cu
numele "StartNotepad.exe".Puteti utiliza acest modul pentru a lansa
programul Notepad.In mod similar,puteti lansa in executie orice alt
program sau fila.De exemplu,daca doriti sa lansati direct ziarul preferat
nu trebuie de cat sa introduceti adresa corecta http:
EXEMPLU:
System.Diagnostics.Process.Start("http:\\gsp.ro\");

-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:

/filealign:number -permite specificarea dimensiunii,pentru fiecare


sectiune din fila produsa.Valorile acceptabile pentru number sunt:
512,1024,2048,4096 si 8192 si reprezinta bytes.Alegerea unei anumite
dimensiuni poate fi esentiala atunci cand aplicatia va fi rulata pe
unitati mici (exemplu: telefon celular).
/optimize[+ sau -] -permite utilizarea unor mecanisme de optimizare a
modulului creat,pentru a fi mai mic,mai rapid,mai eficient.In mod
implicit,compilatorul va utiliza optiunea /optimize-.Se poate combina
cu optiunea /debug.
/doc:file -permite colectarea tuturor comentariilor din cod,pentru a forma
o fila de tip XML (o fila de documentare).Daca fila XML creata are
acelasi nume ca si fila de asamblare din care a fost extrasa si este
amplasata in acelasi director,va putea fi utilizata cu utilitarul
"intelisense",pentru a facilita munca programatorilor Visual C#.
EXEMPLU: csc XMLsample.cs /doc:XMLsample.xml
/out:filename - permite specificarea unui nume pentru fila creata.Daca se
omite fila executabila va avea acelasi nume ca si fila sursa,iar in
cazul filelor DLL si NETMODULE va avea acelasi nume ca si prima fila
din lista filelor sursa.Optiunea se poate utiliza pentru a crea
simultan atat o fila .exe cat si un modul:
EXEMPLU: csc test.cs /out:modul1.netmodule /target:module t2.cs
/target -este optiunea prin care se poate specifica tipul de fila creata.
Are urmatoarele variante:
/target:exe - creaza un executabil de tip consola
/target:library - creaza o biblioteca DLL
/target:module - creaza un modul
/taget:winexe - creaza un program de tip Windows
Daca se creaza simultan mai multe file si module,manifestul va fi
amplasat in prima dintre ele.
EXEMPLU: csc /out:1.exe t1.cs /out:2.netmodule t2.cs
In exemplul de mai sus,manifestul va fi inclus in fila 1.exe.
Nu se pot crea simultan mai multe executabile sau biblioteci DLL.
Daca prima fila este .exe sau .dll,urmatoarele nu pot fi decat
module.Modulele nu sunt considerate a fi asamblari si nu pot contine
manifestul.Pentru a specifica daca fila este CLS complianta se poate
adauga si atributul CLSCompliant.
EXEMPLU:
csc /target:exe test.cs[assembly:System.CLSCompliant(true)]
Daca se omite optiunea /target,se va utiliza implicit /target:exe
Daca fila executabila se construieste din mai multe file,este
suficient ca una dintre ele sa contina functia Main.In acest caz,
se poate utiliza optiunea /main pentru a specifica fila ce contine
functia Main.Pentru filele DLL,functia Main nu este esentiala.Daca
nu se specifica altfel prin /out,biblioteca DLL va purta acelasi
nume ca si fila sursa.
EXEMPLU: csc /target:library test.cs va crea fila test.dll
Varianta target:module permite crearea unei file fara manifest.O
astfel de fila nu poate fi executata,dar poate fi inclusa in alta
asamblare cu ajutorul optiunii /addmodule.

-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.

Acestea sunt principalele optiuni,pentru compilatorul C sharp din


versiunea v3.5 a suportului .Net.Practic,nu exista un singur compilator,
ci fiecare versiune de program are compilatorul sau,iar programele
Visual C# au un compilator diferit.Pot exista diferente semnificative
intre versiuni (majoritatea nu pot fi executate din Visual C#).Este bine
sa verificati cel putin succint ce optiuni sunt incluse in versiunea
d-voastra (cu csc /help).
Exista si un utilitar ce permite dezasamblarea partiala a tuturor
filelor asamblate cu csc.Acest utilitar poarta numele de "idlasm.exe" si
este livrat impreuna cu sistemul de operare,in directorul Microsoft SDKs
(din Program Files).Pentru a descompune un executabil sau o biblioteca
DLL,lansati programul cu un dublu click,apoi utilizati meniul File,pentru
a deschide fila dorita.Exemplu: deschideti fila StartNotepad.exe creata
in primul exemplu din acest manual.Se va deschide continutul filei sub
forma de arbore binar.Puteti observa manifestul si clasa WelcomeCSS.
Daca executati un dublu click pe obiectele din diagrama,se va afisa
continutul obiectului respectiv,in limbaj intermediar.Din meniul View,
puteti selecta o serie de optiuni de afisare.Acest utilitar nu poate
fi utilizat pentru a extrage codurile sursa din fila de asamblare,dar
poate oferii o imagine de ansamblu a spatiilor namespace,a claselor si
metodelor,a proprietatilor si a evenimentelor din program etc.Se poate
utiliza si pentru o inspectie rapida a claselor si metodelor arhivate in
bibliotecile suportului .NET,arhivate la adresa:
C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5
De exemplu,deschideti si explorati fila: System.Core.dll.
-13-
TIPURI DE DATA

Prima aplicatie pentru orice limbaj de programare o reprezinta un


calcul numeric simplu.Primele programe pentru calculator,au fost exclusiv
programe de calcul numeric.Tot din acea perioada,dateaza si formatele
numerice simple: integer,signed si unsigned precum si stivele (stramosii
ariilor de date).Pentru a pastra compatibilitatea cu programele vechi,
majoritatea programelor noi,au pastrat si aceste tipuri de date,chiar
daca utilitatea lor este din ce in ce mai redusa.Pot fi utile,mai ales
in cadrul unor rutine de automatizare,concepute sa opereze in mediu foarte
limitat de memorie.
Limbajul C# recunoaste doua tipuri fundamentale de date : tip valoric
si tip referinta.Tipurile de data valorice pot fi elementare (int,char,
float etc) sau enumerari si structuri.Tipurile referinta (pointerii) pot
fi clase,interfete,clase delegat sau arii de date.In limbaj C# tipurile
predefinite de data sunt membrii ai clasei System,si se importa in
program o data cu biblioteca System.Tipurile valorice contin datele
propriu-zise,in timp ce referintele contin doar un pointer spre adresa
de memorie la care sunt arhivate datele.Este posibil sa existe mai multe
referinte spre aceeasi adresa de memorie.Actualizand datele de la o
astfel de adresa,se vor actualiza automat toate referintele spre acea
adresa.Prin contrast,actualizarea unui tip de data valoric nu poate
modifica nici o alta data din program (data este arhivata in acel tip).
Pentru a facilita munca programatorilor,C# ofera un set destul de
bogat de tipuri predefinite.Toate aceste tipuri predefinite sunt obiecte
si sunt declarate in biblioteca System (Exemple: System.Int16,System.Byte)
iar pentru a simplifica manevrarea lor,au si cate o denumire alias:
-------------------------------------------------------------------------
| Nume | Tip CTS | Descriere | Domeniu de valori |
--------------------------------------------------------------------------
| sbyte | System.SByte | 8-bit signed integer | -128 la 127 |
| short | System.Int16 | 16-bit signed integer | -32768 la 32767 |
| int | System.Int32 | 32-bit signed integer | -2 E31 la 2 E31 |
| long | System.Int64 | 64-bit signed integer | -2 E63 la 3 E63 |
| byte | System.Byte | 8-bit unsigend integer| 0 la 255 |
| ushort | System.UInt16 |16-bit unsigned integer| 0 la 65535 |
| uint | System.UInt32 |32-bit unsigned integer| 0 la 2 E32 (-1) |
| ulong | System.UInt64 |64-bit unsigned integer| 0 la 2 E64 (-1) |
--------------------------------------------------------------------------
| float | System.Single |32-bit single precision| 10 E-45 la 10 E38 |
| double | System.Double |64-bit double precision|10 E-324 la 10E308 |
--------------------------------------------------------------------------
| decimal | System.Decimal |128-bit high precision | 10 E-28 la 10 E28 |
--------------------------------------------------------------------------
| bool | System.Boolean | | true sau false |
--------------------------------------------------------------------------
| char | System.Char |16-bit single character|Unicode character |
--------------------------------------------------------------------------
| object | System.Object | tipul de baza CTS | tipurile derivate|
--------------------------------------------------------------------------
| string | System.String | unicode cahar. string | textul inclus |
--------------------------------------------------------------------------
-14-
Se pot utiliza ambele tipuri de notatii,dar se recomanda utilizarea
notatiilor alias (int,bool,string etc.).
Tipurile de data nu pot fi utilizate ca atare,ci se creaza variabile
sau constante din tipul respectiv.Toate aceste variabile si constante
vor fi de fapt niste obiecte,avand ca ancestor obiectul radacina:
System.Object.Prin acest mecanism,toate datele analizate de compilator
vor fi de tip obiect,adica vor fi incapsulate intr-un spatiu de memorie
denumit (namespace),pentru a evita datele defective rezultate prin
suprascrierea uni tampon de memorie cu valori mai mari decat domeniul
maxim.Daca valorile atribuite nu sunt corecte,obiectul nu se creaza si
se evita astfel aparitia unor date cu format incorect,ce nu pot fi
sterse si paraziteaza inutil memoria.In plus,structurarea sub forma de
obiecte,simplifica mecanismul automat de eliberare a memoriei (garbage
collector).
Exista numeroase modalitati de a controla modul de prezentare a
datelor si interactiunea dintre sistem si utilizator.Dintre acestea,cele
mai simple sunt: Consola si Fereastra Windows.Consola este o fereasta
de tip Windows,dotata cu minimum de metode pentru functii de intrare/
iesire (I/O).Consola este definita in System,sub forma de obiect separat.
Este util sa studiati proprietatile si metodele acestui obiect.
EXEMPLU:
using System;
class Date1 {
public static void Main() {
Console.BackgroundColor = ConsoleColor.Yellow;
Console.ForegroundColor = ConsoleColor.Blue;

Console.WriteLine("Introduceti numele: ");


String name = Console.ReadLine();

Console.WriteLine("Hello, {0}! ",name);


Console.ReadLine();
}}
Salvati fila cu numele Consola1.cs si compilati.
Datele pot fi afisate si intr-o fereastra System.Windows.Forms.
EXEMPLU:
using System;
using System.Drawing;
using System.Windows.Forms;
public class MyForm : System.Windows.Forms.Form {
public MyForm(){}
protected override void OnPaint(PaintEventArgs ev1)
{
Graphics text1 = ev1.Graphics;
Brush creion = new SolidBrush(Color.Blue);
text1.DrawString("Textul dorit:",Font,creion,0,0);
}
static void main() { Application.Run(new MyForm()); }
}
Salvati fila cu numele Exemplu1.cs si compilati.Acest exemplu poate fi
compilat si cu: csc /target:winexe Exemplu1.cs (pt un executabil Windows).

-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;

Console.WriteLine("Introduceti primul numar: ");


double nr1 = System.Convert.ToDouble(Console.ReadLine());
Console WriteLine("Introduceti al doilea numar: ");
double nr2 = System.Convert.ToDouble(Console.ReadLine());

double nr3 = nr1*nr2;


Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Rezultatul este: {0:N}!",nr);
Console.ReadLine();
}}
Salvati fila cu numele Consola2.cs si compilati.
In exemplul de mai sus,se face pur si simplu inmultirea dintre doua
numere.Valorile introduse de la tastatura cu Console.ReadLine() se citesc
intr-un stream de tip string.Prin urmare,trebuie sa fie convertite la
un tip numeric,inainte de a executa orice fel de operatie aritmetica.
Deoarece nu se poate prevedea ce valori doreste utilizatorul,se va prefera
tipul double,pentru a acoperii prectic intregul domeniu de valori
reprezentabile.Tipul de data se poate alege in functie de utilitatea
programului.De exemplu,intr-o situatie concreta,in care valorile din
program sunt mai mici de 250 si sunt strict de tip "integer",nu are
rost sa fie utilizat tipul "double" si se va utiliza tipul de data cel
mai adecvat,adica tipul "byte".In acest caz,tipul "byte" nu numai ca
ocupa mai putina memorie si se executa mai rapid,dar elimina automat
orice valoare mai mare decat domeniul de reprezentare.
Exemplu: pentru a tasta un numar de telefon,nu se vor utiliza decat
cifre de la 0 la 9.

-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;

public class MyForm : System.Windows.Forms.Form{


public MyForm() {}
protected override void OnPaint(PaintEventArgs ev1)
{
double nr1 = 3.141592;
double nr2 = 2.72;
double nr3 = nr1 * nr2;
string t2 = "3.141592 x 2.72 = ";
Graphics text1 = ev1.Graphics;
Brush creion = new SolidBrush(Color.Blue);
text1.DrawString(t2,Font,creion,0,0);
text1.DrawString(System.Convert.ToString(nr3),Font,creion,100,0);
}
static void Main(){ Application.Run(new MyForm());}
}
Salvati fila cu numele Exemplu2.cs si compilati.
Datorita faptului ca toate tipurile de data din C# sunt obiecte,se
spune despre C# ca ofera un sistem unificat de tipuri de data.Ca rezultat
direct,orice tip de data accepta metode mostenite de la tipul radacina
System Object.Astfel,toate tipurile de data pot fi convertite la tipul
string,cu ajutorul metodei ToString().
EXEMPLU: Console.WriteLine(3.ToString));
este perfect corecta si executabila,deoarece si valorile numerice
directe sunt tot obiecte derivate din System.Object.
Acest sistem are importanta si atunci cand se compara doua siruri,fie
ca valori de tip string,fie ca obiecte.
EXEMPLU:
using System;
class Test1 {
static void Main() {
string s = "Test";
string t = string.Copy(s);
Console.WriteLine(s==t);
Console.WriteLine((object)s == (object)t);
Console.ReadLine();
}}
Salvati fila cu numele Consola3.cs si compilati.
Sirul t a fost creat copiat prin clonarea sirului s.Daca se face o
comparatie a celor doua siruri,rezultatul este "true",dar daca se face
o comparatie a celor doua obiecte,rezultatul este "false",deoarece doua
obiecte sunt egale doat atunci cand sunt referinte ale aceluiasi obiect,
sau daca sunt nule (s si t sunt doua obiecte distincte).

-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

Pentru a forma expresii,limbajul C# utilizeaza un set de operatori


comun cu cel utilizat de limbajul C++.In functie de numarul de membri din
expresie,operatorii pot fi: unari,binari sau ternari.Tabelul urmator,
prezinta acesti operatori in ordinea descrescatoare a precedentei.
------------------------------------------------------------------------
| Categorie | Operatori | Asociativitate|
-------------------------------------------------------------------------
| Primari | x.y f(x) a[x] x++ x-- new typeof | stanga-dreapta|
| | checked unchecked | |
-------------------------------------------------------------------------
| Unari | + - ! ~ ++x --x (T)x | stanga-dreapta|
-------------------------------------------------------------------------
| Multiplicare | * / % | stanga-dreapta|
-------------------------------------------------------------------------
| Aditie | + - | stanga-dreapta|
-------------------------------------------------------------------------
| Salt binar | << >> | stanga-dreapta|
-------------------------------------------------------------------------
| Relationali | < > <= >= is as | stanga-dreapta|
-------------------------------------------------------------------------
| Egalitate | == != | stanga-dreapta|
-------------------------------------------------------------------------
| Logic AND | & | stanga-dreapta|
-------------------------------------------------------------------------
| Logic XOR | ^ | stanga-dreapta|
-------------------------------------------------------------------------
| Logic OR | | | stanga-dreapta|
-------------------------------------------------------------------------
| Conditie AND | && | stanga-dreapta|
-------------------------------------------------------------------------
| Conditie OR | || | stanga-dreapta|
-------------------------------------------------------------------------
| Ternar | ? : (conditional) | dreapta-stanga|
--------------------------------------------------------------------------
| Atribuire | = *= /= += -= <<= >>= &= ^= |= | dreapta-stanga|
-------------------------------------------------------------------------

Precedenta si asociativitatea intervin doar atunci cand expresia


contine mai multi operatori.Executia se va face initial in functie de
precedenta:
EXEMPLU: a + b * c <= y + x++
Se executa initial x++,apoi b*c,apoi a+ rezultatul si y+ rezultatul si
in ultimul rand se verifica conditia <=.
Daca in expresie exista mai multi operatori cu aceeasi precedenta (din
aceeasi categorie),atunci ordinea de executie este data de tipul de
asociativitate (stg-dr sau dr-stg).
EXEMPLU: a + b + c + d = x se executa de la stanga la dreapta
a += b -= c *= d se executa de la dreapta la stanga
Pentru a clarifica o expresie,se pot utiliza si parantezele rotunde.

-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()) }

Nu exista diferente esentiale fata de limbajul C++.Alegerea uneia sau


alteia dintre aceste instructiuni,depinde atat de contextul de memorie,
cat si de experienta anterioara a programatorului.In principiu,sunt de
preferat in aplicatiile simple de tip Consola,cu program de tip listing
si sunt de evitat in mediul vizual.In general,nu este recomandabil sa
utilizati bucle automate pentru crearea unor obiecte complexe,deoarece
este foarte greu de controlat alocarea memoriei si exista un risc crescut
de a bloca executia,prin supraincarcarea memoriei de operare.Trebuie sa
stiti,ca instructiunile de procesare au prioritate maxima in ordinea de
executie a procesorului si vor fi executate si atunci cand nu sunt
concepute corect.Este esential sa verificati cu maximum de atentie,toate
situatiile extreme ce pot fi generate de astfel de instructiuni.

-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;

public class Mousedemo:System.Windows.Forms.Form {


public Mousedemo() {
this.MouseUp += new MouseEventHandler(OnMouseup);
}
public void OnMouseup(object sender,MouseEventArgs e) {
this.Text = "Pozitia curenta este: (" +e.X +"," +e.Y +")";
}
public static void Main() {
Application.Run(new Mousedemo());
}
}
Salvati fila cu numele Clasa4.cs si compilati.Lansati programul si
executati cateva click-uri de mouse in fereastra.Se va afisa pozitia (pe
bara de titlu a ferestrei).
In acest caz,se creaza un MouseEventHandler (pentru mesajul OnMouseUp)
si se asociaza obiectului MouseDemo (cel construit prin apelul metodei
constructor).Apoi se declara functia de raspuns la mesaj.Acest mecanism,
este mai greu de inteles decat de implementat.Pur si simplu,puteti aplica
aceasta solutie,pentru orice mesaj Windows.De exemplu,pentru a identifica
apasarea unei taste oarecare,se poate aplica algoritmul:
EXEMPLU:
using System;
using System.Windows.Forms;
using System.Drawing;
public class Keydemo: System.Windows.Forms.Form {
public Keydemo {
this.KeyUp += new KeyEventHandler(OnKeypress);
}
public void OnKeypress(object sender,KeyEventArgs e) {
MessageBox.Show(e.KeyCode.ToString(),"Tasta apasata:");
}
public static void Main() {
Application.Run(new Keydemo());
}
}
Salvati fila cu numele Clasa5.cs si compilati.Lansati programul si
apoi apasati orice tasta.
Observati ca metoda de tratare a evenimentului are si doi parametri:
object sender si EventArgs e.Primul desemneaza obiectul care emite
mesajul Windows,iar cel de al doilea desemneaza obiectul receptor (cel
care interceptreaza mesajul Windows).
Cand programul contine un singur obiect,cu un singur eveniment,nu
exista nici un risc pentru interactiuni nedorite.In mod normal insa,o
interfata grafica contine numeroase obiecte similare,ce emit mesaje
Windows similare.Cei doi parametri au rostul de a face distinctia dintre

-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

Structurile sunt un fel de strabunice ale claselor.De fapt,tipul class


a derivat progresiv din tipul struct,pana cand s-a ajuns la forma actuala.
Deosebirea fundamentala consta in faptul ca structurile sunt date de tip
valoare (sunt obiecte reale) in timp ce clasele sunt date de tip referinta
(sunt obiecte virtuale).Si structurile pot accepta membri si metode,
inclusiv constructori si destructori,dar nu includ si mecanismul de
mostenire,specific claselor.Tipul struct este pastrat ca tip de data,nu
doar pentru compatibilitatea cu programele mai vechi,ci si pentru faptul
ca ofera o solutie mult mai economica,pentru numaroase situatii de
programare.Declararea unei clase si apoi construirea unui obiect din clasa
respectiva,nu numai ca necesita declararea unui pointer,dar ocupa un volum
cel putin dublu de memorie (o data pentru clasa,si apoi pentru obiect).
In toate situatiile in care este necesar un spatiu denumit,doar pentru a
delimita domeniul de vizibilitate al unui grup oarecare de date,este mult
mai economic sa se utilizeze tipul struct,decat tipul class.
La fel ca si clasele,structurile pot organiza grupuri mari de date,
sub forma de stiva,pot include constructori si metode pentru prelucrarea
datelor,dar cu un consum mai mic de memorie.Tipul struct este preferabil
ori de cate ori se cunoaste exact grupul de date cu care se lucreaza si
tipul de operatii ce urmeaza sa fie executate asupra lor.
Prin contrast,tipul class,este recomandabil ori de cate se lucreaza in
mod curent cu un anumit tip de date,dar este probabil ca vor exista mai
multe implementari,sau va fi nevoie de numeroase solutii personalizate.
In plus,clasele sunt esentiale,atunci cand se valorifica solutii standard,
mostenite din clasele definite in bibliotecile standard.

-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

Sunt o inovatie a limbajului C#,cu scopul de a permite transformarea


unei clase,intr-un container indexat la fel ca o arie.Datele vor putea
fi introduse si extrase la fel ca intr-o stiva si vor putea fi sortate
in functie de criteriul de indexare.Nu este obligatoriu ca indexarea sa
se faca prin valori numerice de tip INT.Utilizatorul poate alege orice
alta formula de indexare doreste.
Un indexator (indexer) este un membru al clasei,asemanator cu o
proprietate.La fel ca si proprietatile,indexatorii se declara cu cate o
metoda set() si get() prin care se executa de fapt introducerea sau
extragerea datelor din stiva.
EXEMPLU:
using System;
class Container<T>
{
private T[] arie = new T[100];
public T this[int i]{
get { return arie[i]; }
set { arie[i] = value; }
}}
class ProgramulMeu
{
static void Main(string[] args)
{
Container<string> text1 = new Container<string>();
text1[0] = "text1";
text1[5] = "text5";
text1[7] = "text7";
for(int i=0;i<10;i++){
System.Console.WriteLine(text1[i]); }
Console.ReadLine();
}}
Salvati fila cu numele Indexator1.cs si compilati.
Observati ca pentru declararea indexatorului nu se utilizeaza un cuvant
cheie special,ci se utilizeaza "this",adica pointerul spre obiect.Cu alte
cuvinte,indexatorii nu sunt decat un mic artificiu de programare,mai exact
o metoda de personalizare a clasei respective.
Indexatorii pot fi supraincarcati,sau pot avea mai mult decat un singur
parametru.In exemplul de mai sus,clasa contine un singur parametru de tip
generic <T>,astfel incat in momentul crearii unui obiect sa poata fi
atribuit orice tip de data.Exemplu: pentru a lucra cu date de tip INT,
se poate construi un obiect astfel :
Container<Int32> int1 = new Container<Int32>();
Indexatorii sunt o solutie simpla,atunci cand se lucreaza cu un volum
relativ mic de date,pentru care programatorul doreste o solutie speciala
de indexare.Pentru volume mari de date,exista structuri de date special
concepute,cum sunt iteratorii,listele,listele generice,enumeratorii,
tabelele si bazele de date...etc.
Are rost sa declarati un indexator,doar atunci cand doriti sa efectuati
cateva operatii simple,dupa un algoritm personalizat.

-44-
GENERICE

Genericele nu au fost inventate de limbajul C#.Stilul de programare in


care se utilizeaza date de tip generic,a fost introdus in anul 1983 de
limbajul Ada.Genericele sunt o forma de abstractizare a limbajului prin
care se permite declararea unui set de operatii (algoritm) abstracte,la
care nu se cunoaste tipul de date asupra caruia se opereaza.Operatiile
devin reale,doar in momentul crearii unei instante,in care se declara
explicit si tipul de date.Acest mecanism permite o flexibilitate foarte
mare de programare,mai ales in cazul limbajelor puternic tipizate,cum
sunt C++ si C#.Prin acest mecanism,se poate declara o functie,ce are un
parametru virtual,declarat conventional prin litera T.Acest paramatru
virtual,va putea fi inlocuit in momentul crearii unei instante,cu orice
tip de data.Pentru a semnaliza faptul ca este vorba despre un tip generic
se utilizeaza parantezele ascutite < si > in locul celor rotunde.Se pot
utiliza,unul sau mai multi astfel de parametrii generici,pentru a putea
formula o functie cat mai laxa,ce se poate adapta,in functie de contextul
de memorie din momentul executiei.
EXEMPLU:
using System;
public class Container<T>{
public void Scrie(T input){
Console.WriteLine("S-a adaugat elementul: {0}",input);
}}
class ProgramulMeu{
static void Main(){
Container<string> text1 = new Container<string>();
text1.Scrie("Textul meu");
Container<int> text2 = new Container<int>();
text2.Scrie(33);
Console.ReadLine();
}}
Salvati fila cu numele Generic1.cs si compilati.
Observati ca pentru clasa Container s-a declarat parametrul generic
T.Acest generic a fost apoi utilizat in metoda Scrie().Se poate spune
despre clasa Container ca este o clasa generica.In momentul crearii
unei instante,se va putea preciza tipul de data,pentru a putea executa
un anumit set de operatii.Se observa ca s-au creat doua instante diferite
cu doua tipuri de data diferita,utilizand aceeasi clasa generica.
Pentru a obtine acelasi rezultat cu clase obisnuite,ar fi trebuit sa
fie declarata cate o clasa separata,pentru fiecare tip de data.In plus,
in momentul crearii instantei,ar fi trebuit sa fie incarcata in memorie
si clasa sablon.In cazul claselor generice,se declara si se incarca in
memorie o singura clasa,din care se pot crea apoi oricate instante,
indiferent cu ce tip de data.Prin acest mecanism,se face si o economie
importanta de memorie,atunci cand numarul de instante este foarte mare.
Un generic in care tipul de data este virtual (Exemplu: Container<T>)
se numeste generic "deschis",deoarece poate accepta orice tip de data,
in timp ce un generic in care se precizeaza tipul de data prin inlocuirea
tipului virtual cu unul real (Exemplu: Container<string>) se numeste
generic "inchis",deoarece nu poate accepta decat acel tip de data.

-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;

public class Container<T>{


public void Scrie(T input){
if(typeof(T) == typeof(string)){
Console.WriteLine("S-a adaugat textul: {0}",input);}
if(typeof(T) == typeof(int)){
Console.WriteLine("S-a adaugat numarul INT: {0}",input);}
}}
class ProgramulMeu{
static void Main(){
Container<string> text1 = new Container<string>();
text1.Scrie("Textul meu");
Container<int> text2 = new Container<int>();
text2.Scrie(33);
Console.ReadLine();
}}
Salvati fila cu numele Generic2.cs si executati.
Se observa ca in acest caz,functia Scrie() se comporta diferit daca
instanta este de tip String sau de tip Int.Prin acest mecanism se pot
programa clase si metode "universale",ce pot fi apelate cu orice tip
de data (dintre cele definite in clasa generica),sau se pot programa
"filtre" de date.Clasele generice se pot utiliza cu succes si pentru
tratarea exceptiilor,sau pentru depanarea automata.
Cea mai importanta trasatura a genericelor este capacitatea de a se
modela,in functie de contextul de memorie.Probabil ca aplicatia cea mai
valoroasa o reprezinta capacitatea de a realiza compatibilitatea dintre
doua tabele,sau doua baze de date diferite.Astfel daca doua tabele contin
coloane cu tipuri de date diferite,se poate crea un "filtru",cu ajutorul
unei clase generice,ce va permite conversia automata a datelor si
implicit copierea de date,dintr-un tabel in altul.In mod similar,un astfel
de filtru poate conecta intre ele,arii de date,liste,enumerari si clase
container,sau orice alt tip de structuri de date.Cel mai frecvent,acest
mecanism este util,pentru a putea exploata resurse mai vechi,editate in
alt format sau arhivate in alt tip de container decat cel actual.
Genericele sunt utilizate pe larg si in .Net Framework,fie sub forma
de clase,fie sub forma de interfete,metode,constante si variabile,clase
delegat...etc.Dinte aceste resurse,cele mai frecvent utilizate sunt
clasele generice din biblioteca System.Collections.Generic.Toate aceste
clase sunt specializate pentru un anumit tip de operatii si ofera un
set oarecare de metode standardizate,ce pot fi apelate direct.Clasele
standardizate sunt usor de depanat de catre orice programator sau permit
crearea unor rutine de depanare automata.

-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;

public class Exemplu{


public static void Main(){
List<string> nume = new List<string>();
Console.WriteLine("\n Capacitate: {0}",nume.Capacity);

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;

public class Example{


public static void Main(){

Dictionary<string,string> dic1 = new Dictionary<string,string>();


dic1.Add("Mircea","doctorand");
dic1.Add("Stefan","inginer de sistem");
dic1.Add("Tudor","proiectant de sistem");
dic1.Add("Isabela","studenta");
dic1["Ana"] = "economist";

foreach(KeyValuePair<string,string> pereche in dic1)


{ Console.WriteLine("Cod = {0}, Valoare = {1}",
pereche.Key,pereche.Value); }

Dictionary <int,int> dic2 = new Dictionary<int,int>();


dic2[3] = 33;
dic2[7] = 125;
dic2[11] = 7;
dic2[1] = 22;

foreach( KeyValuePair<int,int> pereche in dic2)


{ Console.WriteLine("Index = {0}, Valoare = {1}",
pereche.Key,pereche.Value); }
Console.ReadLine();
}}
Salvati fila cu numele Generic4.cs si compilati.
In exemplul de mai sus se creaza instante doar pentru doua variante
dintre toate combinatiile posibile.Trebuie remarcat faptul ca fiecare
dintre parametrii poate fi orice tip de data predefinit,sau orice tip de
data definit de utilizator (inclusiv clase,delegati,structuri,arii etc.).
Ca rezultat,numarul de variante este practic infinit.In mod curent insa,
se utilizeaza pentru TKey fie un cod alfanumeric de tip string,fie un
index numeric de tip Int,iar pentru TValue se utilizeaza orice tip de
data.Tipul Dictionary<TKey,TValue>,se utilizeaza pentru a putea identifica
cat mai usor,o anumita inregistrare,in functie de un anumit cod.De exemplu
pentru a crea un utilitar de tip "Help",in care TKey este cuvantul cautat,
iar TValue va fi textul explicativ.
Containerele de tip generic,pot inlocui cu succes tabelele si bazele
de date,atunci cand se lucreaza doar cu date "run time" (efemere).

-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

Enumerarile sunt un tip special de data,definit de catre utilizator.


Este asemanator cu o arie sau cu o lista,in sensul ca poate arhiva si
ordona grupe de date.Fiecare element poate primi o denumire,astfel incat
sa fie cat mai usor de identificat.Enumerarile au rostul de a memora un
set oarecare de valori constante,ce urmeaza sa fie apoi utilizate in
program,pentru setari sau operatii default.Spre deosebire de variabilele
simple,enumerarile garanteaza faptul ca valoarea aleasa fece parte din
setul de valori acceptabile,arhivat anterior.
Biblioteca System,contine o clasa enum,astfel ca declararea unui astfel
de obiect se face folosind cuvantul enum,urmat de lista de elemente cu
virgula intre ele.Fiecare element poate fi o constanta,din orice tip
Integer cu exceptia tipului Char.Daca nu se specifica tipul de data,se
va utiliza implicit tipul Int32.Asadar,o enumerare este un spatiu denumit
ce include o lista de elemente de tip integer.
Clasa enum mosteneste clasa ValueType si implementeaza interfetele
IComparable,IFormattable si IConvertible (vezi referintele),astfel ca
asigura si un set minimal de metode implicite prin care se pot compara
doua instante ale clasei,se pot face conversii de format intre tipul
Integer si tipul String...etc.
EXEMPLU:
using System;
public class Enumerare {
enum Culori { rosu,galben,albastru,verde,alb };

static void Main(){


Console.WriteLine("Primul element este: {0}",Culori.rosu);
Type col = typeof(Culori);
foreach (string s in Enum.GetNames(col))
Console.WriteLine("{0,-11}={1}",s,
Enum.Format(col,Enum.Parse(col,s),"d"));
Console.ReadLine();
}}
Salvati fila cu numele Enumerare1.cs si compilati.
In exemplul de mai sus,se declara o enumerare,apoi se utilizeaza o
bucla foreach,pentru a citi toate elementele din lista,dar si pentru a
face conversia valorilor numerice atribuit automat.Pentru a atribui
alte valori,se utilizeaza operatorul de atribuire "=".
EXEMPLU: enum Culori{ rosu=22,galben=33,albastru,verde=55,alb };
de remarcat este faptul ca in declaratia de mai sus,culoarea albastru
va primi automat valoarea 33 (cea a elementului precedent + 1) ,iar
culoarea alb va primi automat valoarea 56).Pentru verificare,puteti sa
repetati exercitiul cu declaratia de mai sus.
Enumerarile se utilizeaza frecvent,fie pentru a preseta un set de
variabile,fie pentru a verifica daca o valoare oarecare face parte din
setul de valori acceptabile.De exemplu,pentru a crea un filtru de valori,
se declara o enumerare,apoi se apeleaza rutina prin care se verifica daca
valoarea respectiva face parte din enumerare.
Daca se utilizeaza o bucla de tip SWITCH ... CASE este posibil ca
fiecare element din enumerare sa declanseze o anumita functie de raspuns.

-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.

Enumerarile sunt esentiale nu numai ca tip de data in sine,dar si


pentru intelegerea unor structuri de date complexe,cum sunt iteratorii,
sau pentru implementarea unor interfete cum sunt:IEnumerator,IEnumerable.
Tipul enum,este important mai ales pentru a garanta siguranta in
executie a sistemului.Din acest motiv,este esential ca toate operatiile
cu si asupra elementelor din enumerari sa se faca prin rutine automate.
Daca utilizatorul seteaza manual datele din enumerari,sau poate interveni
direct asupra lor,printr-un mecanism oarecare,atunci aceastea nu vor mai
prezenta nici un fel de avantaj fata de o arie de date sau o variabila
simpla.Alegeti acest tip de data,atunci cand doriti sa implementati un
set de rutine automate,independente de interactiunea cu utilizatorul.
Aceasta recomandare este valabila si atunci cand enumerarile sunt
incluse la randul lor in structuri mai complexe (vezi si iteratorii),
sau in procese de setare si actualizare a unei aplicatii.

-51-
ITERATORI

Dupa cum le spune si numele,iteratorii sunt structuri de date destinate


unor operatii repetitive (iterative).Iteratorii nu sunt o inventie a
limbajului C#,ci exista sub o forma sau alta in toate limbajele de
programare.Buclele FOR...NEXT,FOREACH,DO...WHILE sunt exemplele cele mai
simple de operatii iterative.Totusi,in conceptul general de programare
orientata spre obiect,termenul de iterator se utilizeaza doar pentru
obiectelele sau functiile destinate sa parcurga elementele unei colectii,
sau sa permita navigarea prin selectie,sortarea,filtrarea sau selectarea
elementelelor de un anumit tip.Cu alte cuvinte,iterartorii sunt o solutie
software pentru automatizarea unor procese.
In particular,in limbajul C# aceste operatii sunt simplificate prin
existenta unui set de interfete: IEnumerable si IEnumerable<T>,respectiv
IEnumerator si IEnumerator<T>.Aceste interfete definesc un set intreg de
metode standard usor de implementat.Se utilizeaza pentru a selecta si
returna grupuri de elemente dintr-o colectie.Functia prin care se pot
returna datele dorite,prezinta urmatoarele caracteristici:
1.-elementul returnat trebuie sa fie unul din cele patru tipuri mentionate
mai sus: IEnumerable,IEnumerator,IEnumerable<T> sau IEnumerator<T>
2.-pentru a desemna datele returnate se utilizeaza cuvantul "yield" urmat
de cuvantul cheie "return" (yield = a produce,a returna)
3.-fiecare iterator trebuie a fie denumit distinct,la fel ca o clasa.O
clasa poate contine mai multi iteratori.Daca o colectie necesita mai
multi iteratori alternativi,in momentul compilarii se va crea o clasa
speciala pentru fiecare iterator,pentru a permite un mai bun control
al memoriei consumate (se impacheteaza datele).
Cea mai simpla operatie o reprezinta parcurgerea succesiva a datelor
dintr-o colectie.
EXEMPLU:
using System;
using System.Collections;
class ListClass : System.Collections.IEnumerable {
public System.Collections.IEnumerator GetEnumerator()
{ for (int i = 0, i < 10; i++) { yield return i; }
}}
class ProgramulMeu{
static void Main(){
ListClass listClass1 = new ListClass();
foreach( int i in listClass1)
{ System.Console.WriteLine("Valoarea returnata este: "+i);}
Console.ReadLine();
}}
Salvati fila cu numele Iterator1.cs si compilati.
In exemplul de mai sus,clasa ListClass implementeaza o interfata de
tip IEnumerable si supraincarca metoda GetEnumerator() cu o metoda noua
personalizata,in care se genereaza si se returneaza 10 elemente de tip
int.Obiectul listClass1,generat din aceasta clasa este un iterator ce
permite parcurgerea membrilor sai cu ajutorul unei bucle foreach.
Un iterator este foarte usor de recunoscut,de la prima vedere,dupa
cuvantul cheie yield return.

-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

LINQ (Language-Integrated Query) este denumirea prescurtata pentru un


set de tehnologii software ce permit selectarea unor subseturi sau seturi
de date,din structuri de date de tip colectie.In general,o selectie este
o expresie prin care se solicita o preluare de informatii,dintr-o sursa de
date.Acest gen de solutie a fost dezvlotat mai ales pentru prelucrarea de
date arhivate in tabele si in baze de date (date preformatate).Fiecare
sistem de baze de date,a fost conceput cu un anumit format,astfel ca si
selectiile de date au trebuit sa respecte anumite criterii fixe.Cel mai
cunoscut limbaj de acest gen,este SQL (Structured Query Line).LINQ nu
este decat o extensie a limbajului SQL,creata cu scopul de a permite o
flexibilitate crescuta in preluarea de date,din surse formatate diferit.
Astfel,prin selectii LINQ se pot prelua si prelucra date arhivate in
memoria de operare,in tampoane temporare,in arii de date sau in liste,
in file de tip text,XML,in tabele si baze de date,sau in orice alt tip
de suport de memorie.
Tehnologia LINQ implica trei etape diferite:
1.Constructia sursei de date,se poate face astfel:
-se arhiveaza date pe un suport fix
-se preiau date din una sau mai multe surse din retea
-se construiesc date complet noi in memoria de operare
-se convertesc datele existente in mediul de executie
2.Formularea expresiei de interogare (selectie)
3.Executia expresiei LINQ si procesarea datelor preluate din sursa.
Cel mai simplu exemplu,preia datele din memoria de executie.
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class Expresie {
static void Main(){
int[] lista = new int[] { 15,97,92,81,60,3 };
IEnumerable<int> selectie =
from numar in lista where numar < 80 select numar;
Console.WriteLine("Numerele mai mici decat 80 sunt:");
foreach (int i in selectie)
{ Console.Write(i = " "); }
Console.ReadLine();
}}
Salvati exemplul cu numele Linq1.cs si compilati.
O expresie de tip selectie LINQ se formuleaza la fel ca si o expresie
de tip SQL,cu ajutorul unor clauze,ce pot fi declarate intr-o anumita
ordine prestabilita.Astfel,o expresie LINQ trebuie sa inceapa cu clauza
"from",urmata de resursa in care sunt arhivate datele si trebuie sa se
termine cu clauza "select" prin care se pot transforma datele preluate
intr-o secventa noua de date,reformatate sau reformulate.Acest tip de
transformare mai poarta si numele de "proiectie" (projection).Intre cele
doua clauze obligatorii,pot exista una sau mai multe clauze optionale,cum
sunt: where,orderby sau join,prin care se specifica tipul de operatii
necesare pentru a transforma datele preluate.
-56-
De exemplu,in exercitiul anterior clauza select prelucreaza datele
din lista si preia doar pe cele care respecta clauza where.Pentru a
sorta datele si in ordine descendenta,se poate adauga o clauza "orderby".
EXEMPLU:
using System;
using Linq;
using System.Collections.Generic;
class Expresie {
static void Main(){
int[] lista = new int[] { 15,97,92,81,60,3 };
IEnumerable<int> selectie =
from numar in lista
where numar < 80
orderby numar descending
select numar;
Console.WriteLine("Numerele mai mici decat 80 sunt: ");
foreach (int i in selectie)
{ Console.Write(i + " "); }
Console.ReadLine();
}}
Salvati fila cu numele Linq2.cs si compilati.
Se pot combina doua sau mai multe clauze de acelasi tip,sau se pot
combina doua sau mai multe conditii,cu ajutorul operatorilor OR sau AND.
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class Expresie {
static void Main(){
int[] lista = new int[] { 15,97,92,81,60,3 };
IEnumerable<int> selectie =
from numar in lista
where numar < 80 && numar > 10
select numar;
Console.WriteLine("Numerele selectate sunt: ");
foreach (int i in selectie)
{ Console.Write(i + " "); }
Console.ReadLine();
}}
Salvati fila cu numele Linq3.cs si compilati.
Daca sursa de date este la randul sau o colectie de date,se pot utiliza
doua sau mai multe clauze "from",pentru a separa fiecare element.
EXEMPLU: from country in countries
from city in country.Cities
where city.Population > 100000
select city;
va selecta toate obiectele de tip city din fiecare colectie country,
arhivata in colectia countries si va extrage doar pe cele care respecta
conditia din where.
Pentru ca expresia sa evalueze si rezultatul unei alte expresii se
poate utiliza clauza "let".
-57-
EXEMPLU:
using System;
using System.Linq;
using System.Collections.Generic;
class Expresie {
static void Main() {
string[] lista = { "Popescu","Danescu","Stefanescu" };
var selectie = from nume in lista
let subsir = nume.Split(new char[] {'e'})[0]
select subsir;
Console.WriteLine("Rezultatul obtinut este: ");
foreach (string i in selectie)
{ Console.Write(i + " "); }
Console.ReadLine();
}}
Salvati fila cu numele Linq4.cs si compilati.Observati si faptul ca
tipul de data IEnumerable<T> poate fi inlocuit cu succes prin tipul "var".
Pentru a asocia sau combina date preluate din doua surse diferite,se
poate utiliza clauza "join".Selectia se va face cu ajutorul unei operatii
de comparare intre elementele specificate din fiecare sursa de date.
In LINQ,operatii de tip "join" se pot efectua si pe secvente de date ce
contin tipuri diferite de data.Dupa operatia "join" trebuie neaparat sa
urmeze o clauza select sau groupby,prin care sa se specifice elementele
ce urmeaza sa fie preluate din secventa obtinuta prin reuniune.Tipul var,
denumit si tip anonim,poate fi utilizat atunci cand prin operatia de
reuniune doriti sa obtineti un tip de data nou,diferit de cele initiale.
EXEMPLU:
var categoryQuery =
from cat in categories
join prod in products on cat equals prod.Category
select new { Category = cat , Name = prod.Name } ;
In exemplul de mai sus,se vor reuni elementele cat din categories cu
cele prod din products,daca respecta conditia cat=prod.Category si se va
forma o secventa noua de elemente de tip var in care se va arhiva pentru
fiecare element: Category=cat si Name = prod.Name.
Operatiile de tip join sunt aparent simple,dar pot produce numeroase
erori si exceptii,datorate operatiilor cu tipuri diferite de data.Din
acest motiv,este recomandabil sa fie evitate de catre incepatori,sa sa
fie utilizate formule standard,transformate pentru necesitatile din
program (cautati exemple in care se prelucreaza acelasi tip de date si
se utilizeaza acelasi gen de selectie cu cel dorit).Daca construiti
singuri aceste expresii,verificati cu atentie toate valorile posibile din
domeniul de definitie al fiecarui tip de data utilizat.Acest gen de
operatii este principalul generator de erori de executie si de dureri de
cap pentru programator.
Daca este absolut necesar,expresiile pot contine si selectii intricate.
Fiecare noua formula de selectie va incepe cu clauza from si se va termina
cu clauza select.Si acest gen de solutie este mai bine sa fie evitat,
deoarece verificarea si depanarea sunt extrem de greu de controlat.In
toate cazurile este mai simplu daca se utilizeaza doua selectii succesive,
decat o expresie cu doua selectii intricate.

-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

In cadrul activitatii de operare si programare a calculatoarelor,cea


mai importanta componenta este organizarea si gestiunea memoriei.Anual
se produc zeci de mii de resurse software,ce ocupa volume din ce in ce
mai mari de memorie inscriptibila.Organizarea tuturor acestor resurse se
face prin blocuri de memorie denumite,cunoscute sub numele de namespaces.
Fiecare program,aplicatie,resursa software,driver sau container de date,
este inclus intr-un astfel de spatiu de memorie denumit.Chiar si atunci
cand procesorul opereza asupra datelor,creaza automat un astfel de bloc
de memorie temporar,cu un nume de cod generat automat,in care se vor face
toate operatiile de gestiune interna a proceselor.Aceste spatii au o
importanta vitala.Ori de cate ori un bloc de date este mai mare decat
tamponul de memorie alocat in memoria de operare,procesorul va trebui sa
fragmenteze acest bloc in calupuri mai mici,ce vor fi procesate succesiv.
Daca se lucreaza in mediu de multiprocesare,atunci procesorul va avea de
gestionat mai multe astfel de fragmente de cod,ce vor fi prelucrate in
functie de prioritatea thread-ului de executie.Pentru a simplifica la
maximum aceste operatii,programatorii experimentati prefera sa fragmenteze
ei codul sursa,in module astfel dimensionate,incat sa poata fi executate
global.Astfel,cunoscand dimensiunea medie a tampoanelor de memorie din
procesor si din sistemul de operare,se poate calcula dimensiuea optima a
modulelor din program.Exemplul cel mai simplu este date de tabelele si
bazele de date.Presupunand ca memoria RAM este de 32 Mb si baza de date
este o arhiva de 800 Mb,se poate crea un singur tabel de 800 Mb,sau se
pot crea 100 de tabele a cate 8 Mb.In cel de al doilea caz,incarcarea
unui tabel se va face instantaneu,in timp ce in primul caz,incarcarea
datelor poate sa treneze pana cand procesorul creaza un tampon temporar
in care fragmenteaza datele,pe care le prelucreaza apoi succesiv.
In concluzie,cu cat programul este format din blocuri functionale mai
mici,cu atat executia va fi mai rapida.Pentru a fragmenta programul se
utilizeaza spatii denumite.Aceste spatii vor forma fie biblioteci de
resurse de tip DLL,fie module executabile (assemblies).Tot namespaces se
utilizeaza si pentru a fragmenta memoria interna a unui program,atunci
cand se doreste impachetarea datelor in spatii cu vizibilitate redusa.
Crearea unui astfel de bloc de memorie se face cu ajutorul cuvantului
cheie "namespace" urmat de un identificator.
EXEMPLU: namespace BibliotecaMea { ... }
Toate datele incluse in blocul respectiv de memorie vor fi incluse
intre acolade.Intr-un astfel de bloc de memorie se pot include: un alt
namespace,clase,interfete,structuri,enumerari,clase delegat.Toate spatiile
denumite au implicit accesibilitate de tip public,ce nu poate fi
modificata sau restrictionata.Actualizarea datelor se poate face prin
operatii succesive.
EXEMPLU: namespace MyCompany.Proj1 { class MyClass { ...} }
namespace MyCompany.Proj1 { class MyClass1 { ...} }
Dupa cele doua declaratii succesive,blocul MyCompany.Proj1 va include
ambele clase: MyClass si MyClass1.
In mod similar,atunci cand se construieste o biblioteca DLL,se pot
adauga ulterior nenumarate clase,cu conditia sa fie declarate in acelasi
spatiu denumit (vezi exemplul de la optiunile de compilare).

-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

In limbaj C#,toate tipurile valorice sunt stricte,adica nu pot accepta


decat valori incluse in domeniul de reprezentare.Exemplu: tipul Int32
poate accepta orice valoare cuprinsa intre -2147483648 si 2147483647,dar
nu poate accepta valoarea null.
EXEMPLU: int x = null;
Console.WriteLine("x= {0}",x);
va genera o eroare de compilare de genul:
error CS0037: Cannot convert null to int because it is a non-nullable type
Exista insa si numeroase situatii in care este esential ca o variabila
de tip valoric sa poata accepta si tipul null.De exemplu,atunci cand se
citesc valori dintr-o baza de date sau dintr-o resursa oarecare,exista si
posibilitatea ca datele citite sa nu existe.In acest caz,functia prin
care se citesc datele va returna o valoare null.
Pentru a corecta aceasta limitare,C# introduce o structura speciala
denumita System.Nullable.Orice tip de data derivat din System.Nullable
este o data de tip nullable si poate accepta si valoarea null.Pentru a
declara un astfel de tip,se poate utiliza o formula de genul:
System.Nullable<T> variabila
sau prescurtat
T? variabila
Cu alte cuvinte,daca se adauga semnul intrebarii dupa tipul de baza,
data creata va fi de tip nullable.
EXEMPLU:
using System;
class ProgramTest {
static void Main() {
System.Type type = typeof(int?);
Console.WriteLine(" type este din tipul: {0}",type);
int? a = 10;
int? b = null;
Console.WriteLine("a= {0}",a);
Console.WriteLine("b= {0}",b);
a=a+b;
Console.WriteLine("a+b= {0}",a);
Console.ReadLine();
}}
Salvati fila cu numele Nullable1.cs si compilati.
Datele din tipul int? vor accepta toate valorile tipului Int32,la care
se adauga si valoarea null.Daca se fac operatii,tipul null are precedenta.
Conversia din tipul ordinar in tipul nullable,este implicita:
EXEMPLU: bool? a = null;
bool b = true;
a = b; // a primeste valoarea True
Daca se compara intre ele date de tip nullable,orice valoare null va
returna false.Acest lucru este esential atunci cand se construiesc bucle
de tip IF( nullable ).
EXEMPLU: int? num1 = 10;
int? num2 = null;
if (num1 >= num2) { .....} // este evaluata false

-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

In timpul executiei unui program,pot sa apara si situatii ce nu pot fi


anticipate in momentul compilarii.Toate codurile ce pot genera astfel de
situatii neprevazute au fost denumite generic coduri nesigure,sau "unsafe
code".Aplicatiile platformei .Net sunt proiectate mai ales pentru a putea
fi utilizate in mediu de retea,in regim de multiprocesare,unde cea mai
mica eroare de executie se poate amplifica exponential,sau poate afecta
un numar oarecare de utilizatori.Din acest motiv,proiectantii limbajului
C# au decis sa excluda de la compilare toate codurile ce se incadreaza in
aceasta categorie.Cele mai frecvente situatii de acest gen,sunt generate
de pointeri si opertiile cu pointeri.Exemplu: se creeaza automat pointeri
spre diferite adrese si dupa utilizarea lor nu sunt eliminati din memorie
(in C# eliberarea memoriei se face automat doar pentru obiecte).Astfel
memoria va fi fragmentata cu numeroase variabile parazitare.In alte
situatii,se ridica un pointer spre o adresa de memorie,apoi se elibereaza
resursa si ramane un pointer spre o adresa care nu exista.Orice apel al
unui astfel de pointer va determina o bucla infinita de cautare a adresei.
Totusi,exista si numeroase situatii de programare in care este foarte
comod sa se opereze cu si asupra pointerilor.In plus,exista numeroase
coduri scrise in C++,ce pot fi extrapolate si in C#.Pentru a rezolva toate
aceste situatii,codurile nesigure se marcheaza prin cuvantul cheie UNSAFE,
apoi se compileaza adaugand si optiunea /unsafe la comanda de compilare.
Daca se utilizeaza cuvantul cheie UNSAFE in fata unei clase,intreaga
clasa va fi compilata ca si cand ar contine coduri nesigure.In mod similar
se poate marca o interfata,o clasa delegat,o metoda,o proprietate,un
eveniment,un indexator,un operator,un constructor sau un destructor,sau
un bloc oarecare de date inclus intre doua acolade.
Daca se utilizeza blocuri unsafe,se pot utiliza si date de tip
pointer ce se declara adaugand la tipul de data un asterix (*).
Exemple: byte* = pointer to byte int* = pointer to int ...etc.
Daca se utilizeaza doua asterixuri este un pointer spre alt pointer:
EXEMPLU: int** = pointer spre pointer spre int
Practic se utilizeaza aceleasi conventii ca si pentru limbajul C++.
Orice cod preluat din C++,poate fi inclus intr-o bucla unsafe.
EXEMPLU: using System;
class Conversie {
static void Main() {
long numar = 1024;
unsafe {
for (int x=0; x<15; x++) {
numar *= 3;
byte* p = (byte*)&numar;
Console.Write("Numarul: {0} ",numar);
Console.Write(" :conversia in bytes:");
for (int i =0; i < sizeof(long); ++i)
{ Console.Write(" {0:X2}",*p); p++; }
Console.WriteLine(); }
Console.ReadLine()}
}}}
Salvati ca Unsafe1.cs si compliati cu: csc /unsafe Unsafe1.cs

-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

Daca programele sunt mai ample,sau contin coduri cu operatii complexe,


este recomandabil sa fie adaugate si scurte comentarii explicative,ce vor
fi utile pentru orice alt programator care depaneaza sau modernizeaza
programul.Aceste comentarii trebuie sa explice cat mai scut si clar,ce
contine codul respectiv.
Comentariile simple nu sunt suficiente in toate situatiile.Daca fila
respectiva urmeaza sa fie impachetata sub forma de biblioteca DLL sau sub
forma de executabil si nu doriti sa distribuiti si codul sursa,se poate
crea o fila speciala de tip XML,in care includeti toate informatiile
necesare.Aceasta fila,poate fi creata automat in timpul compilarii,daca
se utilizeaza si optiunea /doc si daca fila sursa contine niste comentarii
speciale.Aceste comentarii speciale se introduc prin:
/// - urmate de comentariul dorit pe o singura line
sau prin /** urmat de comentariu pe mai multe linii
iar comentariul se incheie prin grupul */
Fiind vorba despre o fila in format XML,se pot adauga si tag-uri,penrtu
a facilita selectia datelor dorite,dupa o anumita formula CSS.Se pot
utiliza orice fel de tag-uri,dar exista un set de tag-uri conventionale ce
pot fi recunoscute de orice alt programator.Aceste tag-uri conventionale
sunt: <c>, <para>, <see>, <code> , <param>, <seealso>, <example>, <list>,
<paramref>, <summary>, <exception>, <permission>, <typeparam>, <include>,
<remarks>, <typeparamref>, <returns> si <value>.
Fiecare tag se utilizeaza intr-un anumit context.De exemplu,se poate
utiliza <c> text </c> pentru a delimita in cadrul textului o linie de cod
oarecare,sau se poate utiliza <code> text.... </code> pentru a delimita
mai multe linii de cod.Daca se utilizeaza aceasta conventie,utilizatorul
va putea extrage din documentatie doar codurile exemplificative,selectand
doar tag-urile <code> ...</code>.
EXEMPLU:
using System;
// compilare cu csc Xmldoc1.cs /doc:Documentatie.xml
/// text pentru clasa TestClass
public class TestClass
{
///<summary><c> DoWork </c> este o metoda din clasa <c>TestClass</c>
///</summary>
/// <param name="int1"> Parametrul functiei </param>
/// <remarks>
/// Observatii suplimentare despre clasa
/// </remarks>
public static void DoWork(int Int1) {}
/// text explicativ pentru functia Main
static void Main()
{ Console.WriteLine("Test pentru optiunea de compilare /doc");
Console.ReadLine();
}}
Salvati fila cu numele Xmldoc1.cs si apoi compilati cu comanda:
csc Xmldoc1.cs /doc:Xmldoc1.xml
Apoi deschideti si studiati fila Xmldoc1.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

O aplicatie C# completa,include cateva module executabile,cateva file


de resurse si una sau mai multe biblioteci DLL.Cu cat programul este mai
complex,cu atat este bine sa fie fragmentat in module mai mici de memorie
ce pot fi incarcate si executate mult mai rapid.Incarcarea acestor file
in memorie,si apoi eliberarea lor,se face automat,cu ajutorul unor functii
speciale.
In mediul real de lucru,majoritatea utilizatorilor vor executa o astfel
de aplicatie in paralel cu inca una sau mai multe aplicatii similare.Ca
rezultat,memoria va fi incarcata cu un amalgam de file si resurse de la
toate aplicatiile aflate in curs de executie.Mai mult decat atat,este
posibil ca doua sau mai multe aplicatii sa utilizeze aceeasi biblioteca
DLL.Daca una dintre aplicatii elibereaza memoria,fila va fi eliberata din
memorie si va bloca executia celorlalte aplicatii.Pentru a evita astfel de
situatii,si pentru a organiza putin spatiul de memorie si ordinea de
executie,este necesar ca toate datele unei aplicatii sa fie izolate de
cele ale altor aplicatii.

APPLICATION DOMAIN

O metoda foarte flexibila si sigura de a "impacheta" toate datele unei


aplicatii o prezinta clasa AppDomain,creata special pentru acest scop.In
interiorul unei astfel de clase,fiecare aplicatie va putea fi executata
in memorie,intr-un spatiu izolat,ce nu interfereaza cu alte aplicatii
aflate in executie concomitent.Clasa prezinta o serie de metode speciale,
ce permit incarcarea modulelor executabile si respectiv eliberarea
memoriei,dupa algoritmi stabiliti prin linii de program.Cu alte cuvinte,
intr-o astfel de clasa,modulele executabile pot fi incarcate si executate
in ordinea dorita,dupa care memoria poate fi eliberata imediat,fara a mai
astepta mecanismul automat de "garbagge".Acest avantaj este evident mai
ales atunci cand se apeleaza o functie dintr-o biblioteca DLL de proportii
mari.In mod normal,biblioteca se incarca in memorie,se executa functia si
apoi fila paraziteaza memoria pana cand este identificata si eliberata de
mecanismul automat.Daca se utilizeaza o clasa AppDomain,functia dorita
poate fi inclusa intr-un modul executabil mic,ce se incarca in memorie
doar in momentul dorit,se executa functia,dupa care se elibereaza imediat
memoria,pentru operatiile urmatoare.Aceasta economie de memorie pare sa
fie nesemnificativa la prima vedere,dar daca se ia in considerare faptul
ca in mediu de retea exista simultan milioane de utilizatori care apeleaza
astfel de aplicatii,diferenta este mai mult decat semnificativa.Un concept
bun,poate elimina zeci de secunde de asteptare inutila,la fiecare executie
a programului.Principiul de functionare cuprinde trei etape:
1. se creaza o instanta a clasei cu App.Domain.CreateDomain()
2. se incarca si se executa modulul dorit cu ExecuteAssembley()
3. se elibereaza memoria imediat dupa executie cu AppDomain.Unload()
Mai mult decat atat,se poate evita si incarcarea bibliotectii DLL
System,daca apelul functiilor se face specificand si fila sursa ( fila
sursa pentru clasele din System se incarca default).
EXEMPLU: in loc de: Console.WriteLine(....)
se poate utiliza: System.Console.WriteLine(...)

-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

Atributele sunt un mijloc eficient pentru a putea asocia comenzi C#


cu anumite fragmente de cod din program (clase,metode,proprietati etc.).
Atributele se utilizeza pentru a adauga in program metadate,adica niste
informatii suplimentare pentru compilator sau pentru descrierea mai ampla
a unor date din program.Sunt asemanatoare cu directivele de precompilare,
dar sunt mult mai maleabile,deoarece pot fi asociate doar unor fragmente
mici de cod,fara sa influenteze intreaga aplicatie.O data incluse in cod,
atributele pot fi apelate in timpul executiei,printr-un procedeu denumit
"Reflectie" (Reflection) utilizand biblioteca System.Reflection.
Pentru a introduce un atribut,se utilizeaza paranteze drepte.Numele
atributului se introduce intre paranteze drepte,in fata declaratiei cu
care urmeaza sa fie asociat.Prin conventie,numele atributelor se termina
cu "Attribute" pentru a fi cat mai usor de recunoscut in etapa de depanare
a programelor.
EXEMPLU: [SerializableAttribute]
class ClasaMea {...}
In exemplul de mai sus,atributul SerializableAttribute va fi asociat
in etapa ce compilare cu clasa ClasaMea.
Se pot declara si atribute formate din mai multi parametri,separati
intre ei prin virgula.Daca fiecare parametru are un nume distinct,pot fi
introdusi in orice ordine.Daca exista si parametri pozitionali,acestia
trebuie sa fie introdusi intr-o anumita ordine.
EXEMPLU:
[DllImport("user32.dll,SetLastError=false,ExactSpelling=false)]
In atributul de mai sus,user32.dll este pozitional si trebuie neaparat
sa fie primul parametru din atribut,in timp ce urmatorii doi au un nume
distinct si pot fi introdusi in orice ordine.
Un atribut asociaza un set oarecare de date predefinite,cu un fragment
oarecare de cod (un bloc de date).Acest bloc de date,poate fi: un modul
executabil intreg,o clasa,o metoda,un tip de data,un eveniment,o interfata
sau un delegat,o valoare returnata,o proprietate sau chiar un alt atribut.
Pentru declararea atributelor se poate utiliza clasa Attribute si membrii
sai.Metadatele introduse prin atribute pot influenta modul de executie
runtime a programului,sau pot afecta modul in care este procesata intreaga
aplicatie.Unele limbaje de programare folosesc acest procedeu pentru a
introduce in program date ce nu sunt implementate in platforma .Net,dar
sunt predefinite in alte resurse din sistem.

Nu este obligatoriu,dar este comod sa declarati un atribut nou cu


ajutorul unei clase Attribute.
EXEMPLU:
[System.AttributeUsage(System.AttributeTargets.Class |
System.Attributetargets.Struct) ]
public class Author : System.Attribute { }
Prin acest mecanism,clasa ce asociaza atributul,mosteneste si toate
metodele clasei Attribute,iar compilatorul va identifica mult mai usor si
mai rapid metadatele din atribut.
Exista si situatii in care asocierea dintre atribut si coduri poate
fi interpretata ambiguu.
-70-
EXEMPLU: [AtributulMeu]
int Metoda() { return 0; }
In exemplul de mai sus,se poate interpreta asocierea atributului fie cu
Metoda(),fie cu valoarea returnata prin return.In mod normal asocierea
se face cu Metoda(),dar pentru a elimina orice suspiciune se pot utiliza
formule explicite.
EXEMPLU: [method: AtributulMeu]
int Metoda() { return 0; }
sau [return: AtributulMeu]
int Metoda() { return 0; }
Atributele se utilizeaza frecvent in urmatoarele situatii:
1.Atribute conditionale prin care se controleaza fluxul de executie:
EXEMPLU: #define TRACE_ON
using System;
using System.Diagnostics;
public class Trace {
[Conditional("TRACE_ON")]
public static void Msg(string msg)
{ Console.WriteLine(msg); }}
public class ProgramClass {
static void Main(){
Trace.Msg("Se executa functia conditionala !");
Console.ReadLine();
}}
Salvati fila cu numele Atribut1.cs si compilati.
In exemplul de mai sus,Atributul conditional este asociat cu functia
Trace si verifica daca TRACE_ON este definita in program.Pentru a putea
observa diferenta,eliminati directiva de precompilare si apoi compilati
din nou modulul.Acest mecanism poate fi utilizat cu succes in etapa de
depanare a unui program,pentru a verifica daca o variabila oarecare are
sau nu are vizibiliate intr-un anumit spatiu de memorie.Daca se utilizeaza
mai multe atribute simultan,functia va fi executata daca se verifica
oricare dintre ele:
Exemplu: [Conditional("A"),Conditional{"B")]
introduce conditia ca A sau B sa fie definit in memorie

2.Atribute pentru actualizarea aplicatiilor (declara datele obsolete)


EXEMPLU: using System;
[System.Obsolete("A fost inlocuita cu clasa B")]
class A{ public void Metoda() {} }
class B{ [System.Obsolete("Folositi MetodaNoua !",true)]
public void MetodaVeche() {}
public void MetodaNoua() {}
}
class Test{ static void Main(){ A a = new A();
B b = new B();
b.MetodaNoua();
b.MetodaVeche();
}}
Salvati fila cu numele Atribut2.cs si compilati.
Observati ca se returneaza o eroare de compilare si doua avertismente.
-71-
Primul atribut introduce doar un avertisment,iar cel de al doilea
introduce o eroare,prin parametrul "true".Pentru a putea compila acest
modul,exista urmatoarele variante:
1. renuntati la argumente si pastrati datele perimate
2. renuntati la clasa A si la functia MetodaVeche (declarate perimate)
3. pastrati datele perimate dar fara sa apelati clasa sau metoda.

3.Atribute globale prin care se declara metadate valabile pentru intregul


modul.Acest gen de metadate specifica de obicei versiunea,autorul si
firma,drepturile de autor si descrierea tipului de aplicatie.
EXEMPLU: using System;
using System.Reflection;
[assembley: AssembleyVersion("1.0.0.0")]
[assembley: AssembleyTitle("AplicatiaMea")]
[assembley: AssembleyDescription("Mesaj tip Hello World")]
[assembley: AssembleyConfiguration("Configuratie comuna")]
[assembley: AssembleyCompany("Compania Mea")]
[assembley: AssembleyProduct("Aplicatie Test")]
[assembley: AssembleyCopyright("Open source")]
[assembley: AssembleyTrademark("Nu este patentata")]
class Test { static void Main(){
Console.WriteLine("Hello World !");
Console.ReadLine();
}}
Salvati fila cu numele Atribut3.cs si compilati.Metadatele pot fi
consultate cu ildasm.exe in MANIFEST (vezi si numele filei Atribut3.exe)

Pentru a controla modul de utilizare a atributelor se poate apela la


clasa AttributeUsage.
EXEMPLU:
using System;
[AttributeUsage(System.AttributeTargets.Class, AllowMultiple=true)]
class A3: System.Attribute{}

[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

Obiectele de tip container au rostul de a separa anumite grupuri de


date,in spatii cu vizibilitate rezervata.Colectiile sunt un tip special
de containere ce implementeaza si interfata System.Collections.IEnumerable
prin care se genereaza un obiect denumit "enumerator" cu rostul de a putea
naviga intr-un grup de date de acelasi fel.Obiectele de tip colectie,nu
numai ca grupeza date de acelasi tip,dar permit si o serie intreaga de
operatii automate de filtrare,selectare sau sortare a datelor.Obiectele
de tip colectie sunt indispensable atunci cand trebuie facuta legatura
dintre codurile executabile si file de resurse de tip lista,tabel,baza
de date,file XML etc.
Programarea cu obiecte de tip colectie nu este o inovatie a limbajului
C#.Primele obiecte de acest gen au fost stivele simple si dateaza inca
din epoca de pionierat a tehnicii de calcul.Platforma .Net a introdus in
prima etapa,doar obiecte tipizate,de tip stiva,lista sau tabel incrucisat
(hash tables).Ulterior,s-au introdus si colectiile de tip generic,ce
implementeaza una sau mai multe interfete de tip ICollection,IComparer,
IEnumerable,IList,IDictionary sau IDictionaryEnumerator cu scopul de a
dezvolta facilitati noi,cat mai versatile.Obiectele standard de tip
colectie sunt arhivate in System.Collections si System.Collections.Generic
si permit operatii complexe de organizare a grupurilor de date.Cele de
tip generic,sunt mai noi,mai complexe si mai bine structurate,iar cele
tipizate sunt mai simple si mai directe.Alegerea obiectului potrivit se
va face in functie de necesitatile din program.Cel mai simplu obiect de
acest gen,este stiva simpla.Cu un astfel de obiect,elementele unui grup
de date pot fi indexate,sortare,prelucrate independent sau in grup.
EXEMPLU:
using System;
using System.Collections;
public class StivaTest {
public static void Main() {
Queue stiva1 = new Queue();
stiva1.Enqueue("Primul element");
stiva1.Enqueue("Al doilea element");
stiva1.Enqueue("Al treilea element");
Console.WriteLine("Stiva contine valorile: ");
Console.WriteLine("Numar= {0}",stiva1.Count);
Console.WriteLine("Valori=");
Editeaza(stiva1);
Console.ReadLine();
}
public static void Editeaza( IEnumerable colectie1) {
foreach ( Object obj in colectie1 )
Console.WriteLine(" {0}",obj);
}}
Salvati fila cu numele Stiva1.cs si compilati.
Obiectele de tip stiva,sunt rudimentare dar ocupa foarte putina
memorie.Sunt de preferat ori de cate ori sunt necesare doar operatii
simple,fara rearanjarea elementelor in memorie.
EXEMPLE: memorarea unei parole,setari,coduri de acces etc.

-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

O exceptie este o situatie neasteptata,ce intervine in timpul executiei


unui program.O astfel de exceptie poate fi genrata de un cod imperfect,de
incompatibilitati ale datelor preluate din diverse resurse,de sistemul de
operare,de o eroare umana a utilizatorului sau de coruperea accidentala a
datelor.
EXEMPLU: -aplicatia citeste date dintr-o fila de resurse si aceasta fila
este stearsa sau suprascrisa accidental de un alt utilizator.
Nu se pot scrie coduri absolut perfecte,dar o mare parte dintre aceste
accidente de executie pot fi preintampinate.Pentru acest scop,platforma
.Net include o clasa specializata,denumita EXCEPTION.In momentul in care
executia programului este intrerupta de o exceptie,sistemul de operare va
genera automat un obiect de tip EXCEPTION,in care va arhiva pe scurt,tipul
de situatie depistata.In mod normal,executia va fi intrerupta,iar eroarea
va putea fi identificata doar de catre un programator avizat.
De cele mai multe ori,exceptia care a generat intreruperea programului
nu este vitala pentru executia aplicatiei.Din acest motiv,a fost imaginat
un mecanism prin care programul sa poata identifica si corecta eroarea,
pentru a putea continua executia,chiar daca o parte dintre date vor fi
afectate de eroare.Prin acest mecanism,se permite proceselor automate sa
elibereze memoria,sau sa comunice cu alte aplicatii paralele,inainte de a
intrerupe programul pentru remedierea defectiunii.Acest mecanism este
foarte important,mai ales atunci cand mai multe aplicatii conlucreaza in
retea pentru un anumit scop.
Cele mai frecvente erori generate de sistem sunt:
System.ArithmeticException - este generata de o eroare aritmetica
System.ArrayTypeMismatchException - incompatibilitatea tipului de data
System.DivideByZeroException - apare la impartirea prin zero
System.IndexOutOfRangeException - apare la un index negativ sau inexistent
System.InvalidCastException - este generata la erorile de implementare
System.NullReferenceException - apare la apelul unor date nedefinite
System.OutOfMemoryException - apare la supraincarcarea memoriei
System.OverFlowException - apare cand se depaseste domeniul de definitie
System.StackOverFlowException - tamponul de executie este supraincarcat
System.TypeInitializationException - constructorul static emite exceptia

In toate aceste situatii de executie,sistemul de operare identifica


eroarea si construieste automat un obiect de tip Exception,in care descrie
situatia identificata si momentul in care a intervenit.Pentru ca executia
sa nu fie intrerupta,toate situatiile in care este posibil sa apara astfel
de exceptii,pot fi incluse intr-o bucla de tip TRY...CATCH,sau pot fi
conditionate prin atribute,sau prin directive de precompilare.Atributele
au fost prezentate intr-un capitol anterior,asa ca in acest capitol se
vor exemplifica doar buclele TRY...CATCH,desi se pot imagina si alte
solutii pentru tratarea exceptiilor.
Spre deosebire ce C++ si Java,buclele TRY...CATCH din limbajul C sharp,
permit si o bucla suplimentara denumita FINALLY,ce se executa in ambele
situatii: daca intervine sau daca nu intervine o exceptie.Bucla FINALLY
se utilizeaza de obicei pentru operatiile de finalizare a executiei si
eliberare a memoriei (dar se poate exploata si pentru alte scopuri).

-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)

Evolutia tehnicii de calcul a dus la dezvoltarea unor procesoare din


ce in ce mai performante,capabile sa lucreze simultan cu volume din ce
in ce mai mari de informatie.Totusi,memoria de executie a procesorului
este limitata,mult mai mica decat volumul total al programelor prelucrate.
Din acest motiv,sistemul de operare imparte datele ce intra in procesor
in calupuri mai mici,ce urmeaza sa fie procesate succesiv.Fiecare astfel
de bloc de date,nu este impartit dezordonat,ci este inclus intr-un stream,
si este organizat cu ajutorul unui obiect specializat.Aceste obiecte
poarta numele de thread = fir de executie.Aceste fire de executie permit
o serie de operatii:
1. -mai multe aplicatii pot fi executate simultan
2. -se poate stabili o lista de prioritati,astfel incat aplicatiile sa
fie executate preferential atunci cand memoria este limitata
3. -se pot sincroniza doua sau mai multe aplicatii,pentru a organiza
competitia fata de aceleasi file de resurse
4. -se pot executa in background operatii de intretinere,supraveghere si
control,managementul memoriei,depanare automata...etc.
Sistemul de operare Windows,exemplifica cel mai clar acest gen de
operatii.Astfel,utilizatorul poate sa scrie un text,sa asculte muzica si
simultan sa copieze niste date din reteaua Internet.Fiecare aplicatie
este controlata de sistemul Windows cu ajutorul unui thread separat,
incarcat intr-un stream cu executie indepenedenta.Ca rezultat,oricare
dintre aplicatii va putea fi intrerupta sau lansata,in orice moment.
Procesarea paralela poate fi importanta si in cazul aplicatiilor C#.
Presupunand ca aplicatia este formata din mai multe module executabile
mici,acestea vor putea fi lansate in executie cu un AppDomain.
EXEMPLU:
using System;
class Thread1 {
static void Main() {
AppDomain dom1 = AppDomain.CreateDomain("d1");
dom1.ExecuteAssembley(@"c:\v3\Clasa1.exe");
dom1.ExecuteAssembley(@"c:\v3\Clasa3.exe");
AppDomain.Unload(dom1);
}};
Salvati fila cu numele Thread1.cs si compilati.
In exemplul,de mai sus,exista un singur thread de executie (cel creat
automat de sistemul de operare),iar procesarea se face serial.Ca rezultat,
cele doua ferestre nu pot fi deschise simultan.Dupa ce se inchide prima
fereastra se va deschide cea de a doua.In cazul in care cele doua
ferestre trebuie sa fie deschise simultan,solutia este ca fiecare dintre
ele sa fie lansata in executie cu un thread separat.
Platforma .Net,ofera clasa specializata Thread din System.Threading,
cu ajutorul careia se pot construi obiecte de tip thread.Fiecare obiect
va contine metode si proprietati ce permit organizarea fluxului de
executie.
In exemplul de mai sus,se poate crea cate un thread pentru fiecare
dintre cele doua ferestre.In acest mod,fiecare dintre cele doua module
va putea fi executat si controlat separat.

-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;

public class ThreadExemplu {


public static void Proces1() {
for(int i = 0; i < 3; i++){
Console.WriteLine("Proces1- operatia: {0} ",i);
}}
public static void Proces2() {
for(int i = 0; i < 3; i++){
Console.WriteLine("Process2- operatia: {0} ",i);
}}
public static void Proces3() {
for(int i = 0; i < 3; i++){
Console.WriteLine("Process3- operatia: {0} ",i);
}}
public static void Main(){
Console.WriteLine("Incepe thread-ul Main: ");
Thread t1 = new Thread(new ThreadStart(Proces1));
t1.Priority = ThreadPriority.Lowest;
t1.Start();
Console.WriteLine("Firul1 este= {0}",t1.ThreadState);
Thread t2 = new Thread(new ThreadStart(Proces2));
t2.Priority = ThreadPriority.Highest;
t2.Start();
Console.WriteLine("Firul1 este= {0}",t1.ThreadState);
Console.WriteLine("Firul2 este= {0}",t2.ThreadState);
Thread t3 = new Thread(new ThreadStart(Proces3));
t3.Priority = ThreadPriority.Normal;
t3.Start();
Console.WriteLine("Firul1 este= {0} ",t1.ThreadState);
Console.WriteLine("Firul3 este= {0} ",t3.ThreadState);
t1.Join();t2.Join();t3.Join();
Console.WriteLine("Firul1 este= {0} ",t1.ThreadState);
Console.ReadLine();
}}
Salvati exemplul cu numele Thread3.cs si compilati.
In exemplul de mai sus,sunt trei fire de executie cu prioritati
diferite.Se observa cum firul t1,cel cu prioritatea cea mai mica este
stopat pana cand se executa cele doua fire cu prioritate mai mare.Pentru
crearea firelor de executie au utilizat clasa delegat ThreadStart prin
care se conecteaza thread-ul la metoda ce urmeqza sa fie executata (vezi
System.Threading).Metoda Join(),blocheaza fiecare fir,pana la terminarea
executei.

-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

Prin reflexie (reflection) se intelege abilitatea de a putea inspecta


codul programului,in timpul executiei si cea de a putea manipula unele
elemente din program.Aceste abilitati se pot exploata fie pentru a scrie
rutine pentru depanare automata,fie pentru a scrie programe de analiza,de
tip parser.Prin reflexie,se pot:
-crea instante noi ale unui obiect,
-enumera membrii de acelasi fel (acelasi tip de data),ai unui obiect
-executa membrii unui obiect din program
-prelua informatii despre un anumit tip de data
-prelua informatii (metadate) despre un modul executabil
-inspecta atributele unui tip de data
-crea si compila module noi
Clasele specializate pentru acest tip de operatii sunt grupate in:
System.Reflection si System.Reflection.Emit.Principalele clase utilizate
sunt Assembley,Type si Module.
Astfel,pentru a citi metadatele modulului aflat in executie se poate
utiliza metoda Assembley.FullName.
EXEMPLU:
using System;
using System.Reflection;
public class Reflexie {
public static void Main() {
Module[] moduleArray;
moduleArray = Assembley.GetExecutingAssembley().GetModules(false);
Module myModule = moduleArray[0];
Assembly myAssembly = myModule.Assembley;
Console.WriteLine("Modulul curent = {0}.",myAssembly.FullName);
Console.ReadLine();
}
}
Salvati fila cu numele Reflexie1.cs si compilati.
Acest gen de operatie este util in etapa de depanare a unui program,
pentru a identifica ce module sunt incarcate in memorie si ce modul se
afla in executie in momentul unei exceptii,sau al unei erori de executie.
System.Reflection include un numar foarte mare de clase specializate
pentru o anumita operatie.Astfel,doar pentru a culege informatii despre
modulul executabil,exista peste 20 de clase al caror nume incepe cu
Assembley: AssemblyCompanyAttribute,AssemblyFileVersionAttribute...etc.
Alte clase ofera informatii specializate despre membrii clasei: FieldInfo,
MemberInfo,MethodInfo,ParameterInfo etc.Alte clase specializate ofera
date despre procesorul actual: ProcessorArchitecture sau despre locatia
unei resurse: ResourceLocation.
Toate informatiile preluate prin reflexie,pot fi utilizate pentru a
programa bucle de raspuns automat,la anumite situatii ce intervin doar in
etapa de executie a programului.De exemplu,programul poate sa ruleze
module diferite,in functie de arhitectura procesorului utilizat pentru a
rula aplicatia.In alte situatii,se pot programa bucle pentru tratarea
exceptiilor si erorilor generate de un anumit context de executie sau de
o anumita configuratie hardware (depanare automata).
-85-
EXEMPLU:
using System;
using System.Reflection;
public class Reflexie2 {
public int CampDeDate1 = 0;
protected string CampDedate2 = null;
public static void Main(){
FieldInfo[] camp;
Type tip1 = typeof(Reflexie2);
camp = tip1.GetFields(BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Public);
Console.WriteLine("Clasa curenta are urmatoarele campuri de date:");
for(int i = 0; i < camp.Length; i++) {
Console.WriteLine("Nume: {0}",camp[i].Name);
Console.WriteLine("Tip de data: {0}",camp[i].DeclaringType);
Console.WriteLine("Public: ?= {0}",camp[i].IsPublic);
Console.WriteLine("Membru: ?= {0}",camp[i].MemberType);
Console.WriteLine("Tip: {0}",camp[i].FieldType);
Console.WriteLine("IsFamily: ?= {0}",camp[i].IsFamily);
}
Console.ReadLine();
}}
Salvati fila cu numele de Reflexie2.cs si compilati.
In exemplul de mai sus,se returneza campurile de date ale unei clase
si domeniul de vizibiliate.O astfel de operatie este utila pentru a putea
lucra runtime,cu date al caror tip de date este necunoscut,sau pentru a
identifica spatiul de vizibilitate al unei astfel de variabile.
EXEMPLU:
using System;
using System.Reflexion;
[AttributeUsage(System.AttributeTargets.Class,AllowMultiple=true)]

class AtributulMeu : System.Attribute {}

[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);

MethodInfo substr = t.GetMethod("Substring",


new Type[] { typeof(int),typeof(int) }};
Object result =
substr.Invoke("Telefon= 0722-NUMAR",new Object[]{ 9,10});
Console.WriteLine("{0} returned {1}.",substr,result);
Console.ReadLine();
}
}
Salvati fila cu numele Reflexie4.cs si compilati.
In exemplul de mai sus,se invoca metoda Substring(Int32,Int32) pentru
tipul de data string.Se observa in exercitiul de mai sus,obiectul t de tip
Type.Clasa Type este clasa radacina pentru intrega functionalitate a
System.Reflection,iar membrii aceste clase se utilizeaza pentru a obtine
majoritatea informatiilor necesare.(vezi referinta pentru Type)
Clasa Type este o clasa de baza abstracta ce permite implementari
multiple.In timpul executiei,sistemul de operare va crea automat o clasa
derivata din Type,denumita RuntimeType.Aceste clase derivate de tip
Runtime permit doar o impelementare unica per obiect si pot fi incluse in
operatii de comparare.Cu alte cuvinte,obiectele create din clasa Type nu
au identitate decat in momentul executiei.
Clasele System.Reflection pot fi utilizate fara restrictii pentru a
prelua informatii despre membrii publici ai obiectelor din program,dar
necesita o permisiune de tip ReflectionPermission in cazul membrilor
privati si protejati ai obiectelor (adica in cazul membrilor la care
accesul este interzis in mod normal).
System.Reflection.Emit contine o serie de clase destinate pentru a
putea genera module in timpul executiei,sau pentru a putea genera si
metadate in limbaj intermediar Microsoft.Aceste clase se utilizeaza mai
ales pentru a edita comenzi automate pentru compilator,sau pentru a
genera script-uri pentru server.Acest gen de operatii,depasesc nivelul
programatorului incepator si este bine sa fie testate sub observatia unui
programator avansat.Orice eroare inclusa in modulele si script-urile
generate dinamic,nu poate fi identificata sau testata in timp eficient
si poate forma un asa numit "virus informatic".Acest gen de operatii,nu
se recomanda decat pentru programatorii profesionisti.Exemplu: pentru
a seta algoritmul de raspuns al unui server la o anumita situatie din
retea.
In sinteza,reflexia este un procedeu de autoinspectie a codurilor
din program,ca un fel de imagine in oglinda a unei persoane.Este destinata
mai ales pentru programe de analiza,sau verificare si control automat al
mediului de executie.

-87-
SECURITATEA APLICATIILOR

Prin notiunea de securitate a unei aplicatii se intelege siguranta in


exploatare.Siguranta in executie a unei aplicatii nu este o entitate
distincta,ci este doar o componenta a unui context mult mai larg,format
din securitatea sistemului de operare.In cazul calculatoarelor izolate,
notiunile de securitate se rezuma la un minimum de verificari de format
al datelor si la operatii simple pentru managementul memoriei.In cazul
retelelor de calculatoare,probelemele de securitate ale sistemului sunt
cu atat mai mari,cu cat creste complexitatea retelei si numarul de
utilizatori.Din acest motiv,masurile de securitate se pot impartii in
nivele de securitate. Exemple:
1. -masuri valabile pentru un anumit utilizator
2. -masuri valabile pentru un grup de utilizatori
3. -masuri ce afecteaza toti utilizatorii unei anumite unitati
4. -masuri ce afecteaza o anumita aplicatie
5. -masuri ce intereseaza grupuri de aplicatii dintr-o unitate
6. -masuri ce intereseaza grupuri de calculatoare din retea
7. -masuri ce intereseaza administratorii de sistem
... etc.
Nivelele de securitate se pot forma in functie de criteriile de
selectie.Toate masurile de securitate se concretizeaza prin anumite tipuri
de restrictii,selective sau supraselective.Aplicatiile nu castiga nici un
plus de functionalitate,dar se elimina eventualele erori de executie ale
unor utilizatori neavizati,sau se previne scurgerea de informatii utile
spre persoanele neautorizate.Din acest motiv,nu se vor programa seturi de
astfel de masuri,decat atunci cand sunt strict necesare.In mod normal,
aplicatiile de tip "open source",destinate pentru a fi executate pe
unitati de tip monopost,nu necesita nici un fel de masuri speciale de
securizare.In schimb,aplicatiile de tip Web,destinate pentru reteaua
Internet,trebuie sa respecte masurile standard de securizare.Aceste
masuri standardizate,sunt valabile pentru orice limbaj de programare.
Limbajul C# nu adauga nici un fel de masuri speciale sau noi,ci doar
exploateza seturile de clase si intefete oferite de platforma .NET.
Platforma .NET ofera urmatoarele biblioteci de clase specializate:
Security, Security.AccessControl, Security.Authentication, Security.
ExtendedProtection, Security.ExtendedProtection.Configuration, Security.
Cryptography, Security.Cryptography.Pkcs, Security.Cryptography.Xml,
Security.Cryptography.X509Certificates, Security.Permissions, Security.
Policy, Security.Principal si Security.RightsManagement.
Toate aceste biblioteci DLL,contin seturi de resurse ce permit tot
arsenalul de masuri de securitate,de la cele simple,pana la codificarile
complexe.In acest manual nu vor fi prezentate decat cateva exemple
simpliste,de interes general.In mod normal,securitatea de exploatare a
sistemului este asigurata prin firme specializate,ce combina masurile
de tip software,cu cele de tip hardware.Din acest motiv,ori de cate ori
doriti sa securizati o aplicatie,este recomandabil sa apelati la o firma
specializata.Cu atat mai mult,atunci cand aplicatia arhiveaza sau
prelucreaza date cu caracter economic sau secret.Puteti insa implementa si
masuri de securizare personalizate,atunci cand doriti sa limitati accesul
la date pentru utilizatori neautorizati (persoane sau alte calculatoare).

-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;

public class Exemplu {


public static void Main(){
Console.WriteLine("Introduceti parola:");
string sir1 = Console.ReadLine();
SecureString sec1 = new SecureString();
foreach (char ch in sir1)
sec1.AppendChar(ch);
Console.WriteLine("Textul securizat este: ");
Console.WriteLine(sec1);
Console.WriteLine("Parola desecurizata este: ");
Console.WriteLine(sir1.ToString());
Console.ReadLine();
}}
Salvati fila cu numele Security1.cs si compilati.
O alta clasa frecvent utilizata este PermissionSet() cu ajutorul careia
se pot introduce in mediul de executie diverse permisiuni:
EXEMPLU: using System;
using System.Reflection;
using System.Security.Permissions;
using System.Security;
using System.IO;
using System.Collections;
class ClasaTest {
public static void PermissionSetDemo() {
PermissionSet ps1 = new PermissionSet(PermissionState.None);
Console.WriteLine("Adauga un set de permisiuni: ");
ps1.AddPermision(new EnvironmentPermission(
EnvironmentPermissionAccess.Read,"USERNAME"));
ps1.AddPermision(new EnvironmentPermission(
EnvironmentPermissionAccess.Read,"COMPUTERNAME"));
Console.WriteLine("Numar de permisiuni = " + ps1.Count);
Console.WriteLine("Proprietate SyncRoot = " + ps1.SyncRoot);
}
static void Main(string[] args) {
PermissionSetDemo();
Console.ReadLine();
}}
Salvati fila cu numele Security2.cs si compilati.

-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;

public class ExempluDeEvidenta {


public bool CreateEvidence() {
bool retVal = true;
Evidence ev1 = new Evidence(null);
Console.WriteLine("Se creaza evidenta1: ");
Url url = new Url("http://www.BlogulMeu.com");
Url url2 = new Url("http://www.ZiarulLocal.com");
Url url3 = new Url("http://www.CompaniaMea.com");
ev1.AddHost(url);
ev1.AddHost(url2);
ev1.AddHost(url3);
PrintEvidence(ev1).ToString();
return retVal;
}
public static int PrintEvidence(Evidence myEvidence) {
int p = 0;
Console.WriteLine("Adresele din evidenta sunt: ");
if ( null == MyEvidence) return 0;
IEnumerator list = myEvidence.GetEnumerator();
while (list.MoveNext())
{ Console.WriteLine( list.Current.ToString()); }
return p;
}
public static void Main() {
ExempluDeEvidenta Test = new ExempluDeEvidenta();
bool ret = Test.CreateEvidence();
Console.ReadLine();
}}
Salvati fila cu numele Security4.cs si compilati.
In mod similar,se poate edita dinamic polita de restrictii si sau de
permisiuni,pentru toate resursele din aplicatie,in functie de profilul
userului.
In mod normal,nu se aplica masuri de securitate decat pentru societati
comerciale,comunicatii speciale criptate sau pentru informatiile cu
caracter secret.Masurile de securitate standard din reteaua Web,sunt de
fapt seturi de recomandari,ce pot fi modificate in functie de necesitatile
din viata de zi cu zi.Fiind vorba despre un domeniu destul de sensibil,
este recomandabil sa consultati o firma de specialitate,ori de cate ori
trebuie sa rezolvati sau sa reglementati restrictiile de acces pentru
aplicatii ce urmeza sa ruleze in retea.

-91-
PLATFORMA .NET

Platforma .NET este un produs software,destinat pentru sistemul de


operare Windows,cu scopul de a produce aplicatii de tip Web si XML.Este
un concept radical diferit fata de produsele anterioare,prin faptul ca
utilizeaza un limbaj intermediar,comun pentru toate limbajele compatibile
cu sistemul Windows.Toate aplicatiile,idiferent in ce limbaj au fost
scrise,urmeaza sa fie compilate pentru a genera un executabil ce contine
coduri simbolice denumite generic MSIL (Microsoft intermediate language).
Platforma include si interpretor al acestui limbaj intermediar,denumit
generic CLR (Common Language Runtime),care asigura mediul de executie si
transforma codul intermediar in cod masina.Ca rezultat,modulele editate
in limbaj C# pot fi combinate cu module scrise in Visual Basic sau in
Java#,pentru a forma aplicatii complexe.Cu alte cuvinte,CLR este ca un
fel de executabil,ce deschide o fereastra (un container) in care urmeaza
sa fie citite si interpretate datele ce formeaza aplicatia.Prin acest
mecanism,se construieste in memorie un mediu de executie configurat
special,izolat de restul memoriei,in care datele de tip MSIL sunt
transformate in coduri masina.Acest mediu de executie,este de fapt un
tampon de memorie,ce poate fi procesat local,sau poate fi expediat la
orice adresa de memorie din retea.Astfel,o aplicatie .NET poate fi
executata si arhivata local,poate fi executata local si expediata la o
alta adresa de memorie din retea,sau poate fi executata la multiple adrese
din retea (adica este portabila).Singura conditie este ca la adresa de
destinatie sa existe o copie a CLR (a interpretorului).
Pe langa interpretor,platforma .NET asigura si o biblioteca extensiva
de resurse software,formata din clase,interfete,enumerari si executabile
(tools = instrumente software).Toate aceste resurse,contin principalele
tipuri de data si functii specializate pentru majoritatea operatiilor
ce se pot executa asupra acestor tipuri de data.Aplicatia nu mai trebuie
sa defineasca tipuri de data si functii,ci doar le apeleaza pe cele
standardizate.Astfel,codurile vor fi mult mai simple,mai scurte si mai
usor de verificat si depanat.
Principalele avantaje oferite de platforma .NET sunt urmatoarele:
1. -se pot combina usor modulele executabile create de autori diferiti,
de la firme si companii diferite,scrise in limbaje de programare diferite.
2. -se creaza un standard industrial comun,pentru aplicatiile ce implica
si schimb de date,sau comunicatii active in reteaua Internet.
3. -aplicatiile platformei .NET sunt puternic obiectuale,datele sunt
foarte bine protejate in nenumarate containere,asigurand astfel o foarte
buna securitate in executie.
4. -aplicatiile platformei .NET se preteaza foarte bine la orice proces
de automatizare si control automat
5. -CLR asigura managementul complet automat la memoriei cu ajutorul unui
mecanism de tip "garbage collector" asemanator cu cel din Java.
6. -asigura o compatibilitate perfecta intre diferitele versiuni ale
bibliotecilor DLL.Pentru a actualiza o astfel de biblioteca DLL,codurile
noi se adauga langa cele vechi,pastrand si functionalitatea celor perimate
7. -permite crearea unor executabile dinamice,care exista doar pentru
putin timp,doar in memoria de executie.Se pot executa astfel operatii
complexe,fara a consuma nici un byte de memorie inscriptibila.

-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:

COMMON LANGUAGE RUNTIME

Este un sistem de operare in miniatura.Contine toate elementele


necesare pentru contentionarea si prelucrarea datelor dintr-o aplicatie.
Lucreaza cu module executabile denumite asamblari (assembly) produse de
compilatorul specializat al fiecarui limbaj de programare (csc.exe in
cazul limbajului C#).Pentru prelucrarea datelor,contine un interpretor al
limbajului MSIL,dar si un mecanism complex prin care identifica si apoi
conecteaza resursele software.Legatura dintre coduri si bibliotecile de
resurse in care se afla definitia lor,se face cu ajutorul unor metadate,
generate de compilator in momentul in care se construieste modulul
executabil.CLR,citeste metadatele din modul si apoi preia codurile necesare
din bibliotecile de clase.Dupa ce aduna si asambleaza toate elementele
necesare,CLR transforma codul intemediar in cod masina (denumit "managed
code") ce urmeaza sa fie trimis spre executie.CLR organizeaza toate
obiectele si referintele spre obiecte si tine o evidenta stricta a lor.
In momentul in care un obiect,sau o referinta nu mai este utila in
program,aceasta va fi mutat intr-un tampon de memorie auxiliar,de unde
urmeaza sa fie apoi eliberat (sters din memorie).Practic,exista mai
multe astfel de tampoane de memorie,in care datele sunt arhivate "pe
linie de asteptare",pana cand procesorul gaseste o bresa in executie
pentru eliberarea lor.Aceste tampoane au o prioritate de executie,in
functie de vechimea si importanta lor.Daca exista suficienta memorie de
executie,procesorul va executa prioritar orice alta operatie din stiva
sa de functii,dar daca memoria de executie este plina,vor avea prioritate
operatiile de eliberare a memoriei.Acest mecanism de autocontrol este
denumit generic "garbage collection" (colectarea gunoiului) si a fost
introdus pentru prima data in practica,de mediul Java.
CLR poate prelucra simultan doua sau mai multe module executabile,
chiar daca au fost generate de compilatoare diferite.Biblioteca de clase
este aceeasi,limbajul intermediar este comun,asa ca nu mai trebuie decat
sa citeasca metadatele si sa creeze conexiunile necesare dintre resurse
si coduri.In acest fel,modulele scrise in limbaje diferite pot functiona
sinergic,pentru a forma aplicatii noi.
Interpretorul pentru codurile MSIL poarta si numele de compilator JIT
(Just In Time),sau compilator instantaneu.Codurile masina sunt diferite
de la un procesor la altul,asa ca CLR trebuie sa includa cate un astfel
de compilator pentru fiecare tip de arhitectura hardware a procesorului
instalat.Este posibil ca aplicatiile .NET sa nu poata fi executate,doar
atunci cand sunt prelucrate pe un calculator cu procesor diferit de
arhitectura ce formeaza standardul comercial.Este posibil ca aplicatia
sa contina si numeroase coduri MSIL,ce nu vor fi executate decat optional
sau alternativ.Compilatorul JIT nu converteste decat codurile cu executie
imediata,crescand astfel viteza si randamentul executiei,apoi memoreaza
codurile convertite atunci cand urmeaza sa fie executate repetat.

-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.

CLASS LIBRARY (bibliotecile standard)

Pentru a creste cat mai mult functionalitatea programelor,platforma


.NET ofera o colectie foarte bogata de coduri predefinite,arhivate intr-o
serie de biblioteci DLL (biblioteci cu alocare dinamica).Aceste biblioteci
contin clase,interfete si colectii de date de tip valoare.
Pentru a simplifica mamagementul memoriei,datele din biblioteci sunt
separate in containere mai mici (namespaces) cu o denumire simpla si
clara.Aceste containere sunt incluse unul in altul,intr-o ordine de tip
ierarhic,structurata dupa conexiunile ce exista intre ele.Pentru a putea
utiliza datele,se va utiliza o sintaxa in care se specifica atat numele
containerului cat si numele tipului de data apelat.
EXEMPLU: System.Collections.ArrayList desemneaza tipul de date ArrayList,
arhivat in containerul System.Collections.
Daca containerele sunt incluse unul in altul,calea de acces se va
construi cu ajutorul operatorului punct (.).
EXEMPLU:
namespace System {
namespace Collections{ ...}
}
se va scrie: System.Collections
Trebuie remarcat faptul ca o singura biblioteca poate contine numeroase
astfel de containere.Pentru a economisi memoria consumata,se poate
importa strict containerul dorit,fara a incarca intreaga fila DLL.
Cel mai important dintre containere poarta numele de System si contine
toate tipurile esentiale de data,definite sub forma de clase.Aici sunt
definite tipurile: Object,Byte,Char,Array,Int32,String...etc.Practic,
nici o aplicatie nu poate fi executata fara aceasta resursa,decat data
fiecare tip de data este definit explicit de catre utilizator.

-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

Contine clasele necesare pentru construirea unei interfete grafice cu


utilizatorul,de tip Windows.Clasele sunt organizate ierarhic.Majoritatea
lor deriva din clasa Control si mostenesc toate metodele acestei clase.
O parte dintre ele formeaza ferestre si controale destinate pentru
interactiunea cu utilizatorul,altele formeaza meniuri si bare de meniu.
Exista si controale specializate pentru operatii I/O,sau pentru transferul
datelor precum si controale specializate pentru afisarea datelor fie sub
forma de text si tabele,fie sub forma grafica.Numarul acestor clase si
paleta de proprietati si metode,poate fi diferita de la o versiune la
alta (versiunile noi adauga functionalitati noi).
Clasa de baza pentru orice interfata Windows o reprezinta fereatra
simpla,adica un obiect de tip Form.Aceasta fereastra va functiona pe post
de suport,(container) pentru toate obiectele din interfata.

-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;

public class AWindow: System.Windows.Forms.Form{


Button buton1;
RichTextBox text1;
public AWindow(){ Initializeaza(); }
private void Initializeaza(){
this.Size = new Size(900,700);
buton1 = new Button();
buton1.Text = "Open File";
buton1.Location = new Point (20,10);
buton1.Size = new Size(72,24);
buton1.Click += new EventHandler (button_Click);
text1 = new RichTextBox();
text1.Location = new Point(30,40);
text1.Size = new Size(850,600);
text1.Font = new Font("Verdana",12,FontStyle.Bold);
this.Controls.AddRange(new Control[] { buton1,text1});
this.ShowDialog(); }
private void button_Click(object sender, EventArgs e) {
Stream myStream = null;
OpenFileDialog fila1 = new OpenFileDialog();
fila1.InitialDirectory = "c:\\";
fila1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
fila1.FilterIndex = 2;
fila1.RestoreDirectory = true;
if (fila1.ShowDialog() == DialogResult.OK) {
try { if ((myStream = fila1.OpenFile()) != null)
{ using (myStream)
{ text1.LoadFile(fila1.Filename,RichTextBoxStreamType.PlainText); }
}}
catch (Exception ex)
{ MessageBox.Show("Nu gasesc fila.Eroare: " + ex.Message); }}}
static void Main(){ AWindow fereastra1 = new AWindow(); }
}
Salvati fila cu numele Window4.cs si compilati.In acest exemplu,pe
langa buton,se adauga un obiect de afisaj de tip RichTextBox si un
obiect de conectare de tip OpenFileDialog.La apasarea butonului se va
deschide caseta de dialog,in care utilizatorul poate alege orice fila
de tip text,pentru a fi afisata in RichTextBox.Obiectul de conectare,
are un filtru,prin care puteti limita accesul,doar la un anumit tip de
file.Exemplu: puteti arhiva in program cateva file cu extensia .xxx si
apoi setati filtrul pentru a deschide doar filele de tip .xxx.Cu un
astfel de control,utilizatorul va avea doar acces de tip Read-Only,la
un anumit subset de file,alese de programator.
Multe aplicatii executa si operatii cu date calendaristice.Pentru a
afisa data curenta,sau o data oarecare,exista clasa DateTimePicker.
-98-
EXEMPLU:
using System;
using System.Windows.Forms;

public class AWindow: System.Windows.Forms.Form {


public AWindow(){
DateTimePicker data1 = new DateTimePicker();
data1.MinDate = new DateTime(1900,1,1);
data1.MaxDate = DateTime.Today;
data1.CustomFormat = "MMMM dd,yyyy - dddd";
data1.Format = DateTimePickerFormat.Custom;
data1.ShowCheckBox = true;
data1.ShowUpDown = true;
Controls.Add(data1);
this.ShowDialog();
}
static void Main(){ AWindow a1 = new AWindow(); }
}
Salvati fila cu numele Window5.cs si compilati.Cu un astfel de
control,utilizatorul poate alege o data oarecare,din intervalul setat
intre MinDate si MaxDate.Apoi,data respectiva poate fi preluata si
procesata pentru a produce rezultatele dorite.
Unele controale pot contabiliza operatiile din interfata:
EXEMPLU:
using System;
using System.Windows.Forms;
public class Form1 : Form {
public static void Main() { Application.Run(new Form1()); }
private Button button1;
private ListBox listBox1;
public Form1() {
button1 = new Button();
button1.Left = 200;
button1.Text = "Exit";
button1.Click += new EventHandler(button1_Click);
listBox1 = new ListBox();
this.Controls.Add(button1);
this.Controls.Add(listBox1);
}
private void button1_Click(object sender,System.EventArgs e)
{ int count = 1;
while ( MessageBox.Show("Exit ?","",
MessageBoxButtons.YesNo) == DialogResult.No)
{ listBox1.Items.Add(count); count += 1; }
Application.Exit();
}}
Salvati fila cu numele Window6.cs si compilati.La fiecare selectie
a butonului No,caseta ListBox va inregistra o noua operatie.Observati
modul in care a fost implementata conditia optionala pentru fereastra
MessageBox.Intreaga functionalitate a interfetei este conectata prin
aceasta bucla while().
-99-
Exista si situatii in care doriti ca aplicatia sa deschida in paralel
doua,sau mai multe ferestre simultan.Solutia este sa includeti toate
ferestrele intr-un obiect de tip Application si sa utilizati thread-uri
diferite pentru fiecare fereastra.
EXEMPLU:
using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

public class AWindow : System.Windows.Forms.Form {


public AWindow(){ this.BackColor = Color.Red; } }
public class AWindow2 : System.Windows.Forms.Form {
public AWindow2(){ this.BackColor = Color.Blue; } }
class Aplicatie {
public void F1(){ Application.Run(new AWindow()); }
public void F2(){ Application.Run(new AWindow2()); }
static void Main() {
Aplicatie a1 = new Aplicatie();
Thread t1 = new Thread(a1.F1);
Thread t2 = new Thread(a1.F2);
t1.Start(); t2.Start();
}
}
Salvati fila cu numele Window7.cs si compilati.In mod similar,se poate
utiliza cate un thread separat pentru fiecare eveniment din interfata.In
acest caz,programul va putea executa in paralel mai multe seturi de
operatii,crescand viteza de executie si securitatea datelor.
Fereastra poate beneficia si de un meniu functional:
EXEMPLU: using System;
using System.Windows.Forms;
using System.Drawing;
public class AWindow : System.Windows.Forms.Form{
public AWindow(){ MainMenu meniu = new MainMenu();
MenuItem optiune1 = new MenuItem();
MenuItem optiune2 = new MenuItem();
optiune1.Text = "&File";
optiune2.Text = "&Open";
optiune1.MenuItems.Add(optiune2);
meniu.MenuItems.Add(optiune1);
optiune2.Click += new System.EventHandler(this.optiune2_Click);
this.Menu = meniu; }
private void optiune2_Click(object sender,System.EventArgs e)
( OpenFileDialog fd = new OpenFileDialog();
fd.DefaultExt = "*.*";
fd.ShowDialog(); }
static void Main(){ Application.Run(new AWindow()); } }
Slavati fila cu numele Window8.cs si compilati.Obsrevati etapele de
lucru: 1.-se creaza meniul 2.-se creaza optiunile 3.-se grupeaza optiunile
in seturi de optiuni 4.-se include meniul in fereastra (cu Menu=...) si
5.-se definesc evenimentele OnClick pentru fiecare optiune.

-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;

public class Lista1 : System.Windows.Forms.Form {


public Lista1(){
this.Size = new Size(800,400);
ComboBox combo = new ComboBox();
combo.Location = new Point(10,10);
combo.Size = new System.Drawing.Size(300,20);
this.Controls.Add(combo);
string path = @"c:\Fructe.txt";
string[] readtext = File.ReadAllLines(path);
foreach( string s in readtext)
{ combo.Items.Add(s); } }
public static void Main(){ Application.Run(new Lista1()); }}
Salvati fila cu numele Window9.cs si compilati.Observati ca pentru
calea de acces spre resursa se adauga si @ (pentru a forma un pointer).
Casetele de dialog se pot popula si cu date preluate din tabele si baze
de date,sau chiar cu date preluate din retea,de la diferite adrese Web,
dar filele de tip text arhivate local asigura un grad sporit de siguranta.
Clasa ColorDialog permite utilizatorului sa personalizeze aspectul
interfetei grafice,dupa bunul plac:
EXEMPLU: using System;
using System.Windows.Forms;
using System.Drawing;
public class AWindow : System.Windows.Forms.Form {
public AWindow() {
Button buton1 = new Button();
buton1.Text = "Alegeti culoarea";
buton1.Location = new Point(70,100);
buton1.Size = new Size(150,24);
buton1.Click += new EventHandler (button_Click);
this.Controls.AddRange( new Control[] {buton1});
this.ShowDialog(); }
private void button_Click(object sender,EventArgs e) {
ColorDialog dialog1 = new ColorDialog();
dialog1.ShowHelp = true;
if (dialog1.ShowDialog() == DialogResult.OK)
this.BackColor = dialog.Color;
static void Main(){ Application.Run(new AWindow()); } }
Salvati fila cu numele Window10.cs si compilati.Aplicatia poate
contine o fereastra separata cu butoane,destinata special pentru
ca utilizatorul sa poata personaliza aspectul interfetei grafice.In acest
caz,setarile se arhiveaza local si se incarca la fiecare utilizare.

-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;

public class Form1: System.Windows.Forms.Form {


private MonthCalendar calendar1;
static void Main(){ Application.Run(new Form1()); }
public Form1(){
calendar1 = new MonthCalendar();
calendar1.Location = new Point(47,16);
calendar1.CalendarDimensions = new Size(4,3);
calendar1.FirstDayOfWeek = System.Windows.Forms.Day.Monday;
calendar1.MaxDate = new DateTime(2100,12,31,0,0,0,0);
calendar1.MinDate = new DateTime(1900,1,1,0,0,0,0);
calendar1.ScrollChange = 12;
calendar1.ShowToday = true;
calendar1.ShowWeekNumbers = true;
calendar1.ShowTodayCircle = true;
ClientSize = new Size(800,500);
this.Controls.AddRange(new Control[] { calendar1});
this.Text = "Exemplu de calendar multianual";
}}
Salvati fila cu numele Window11.cs si compilati.O astfel de fereastra
independenta prezinta datele calendaristice doar orientativ.Daca este
necesar sa se execute operatii aritmetice cu data aleasa de utilizator,
se poate adauga o metoda suplimentara de genul:
calendar1_DataSchimbata(object sender,EventArgs e){
this.text1 = e.Start.ToShortDateString(); }
metoda se va conecta in Form1() cu un event handler:
this.calendar1.DateChanged +=
new System.Windows.DateRangeEventHandler(this.calendar1_DataSchimbata);
unde text1 este un string sau o caseta TextBox pentru afisarea datei.
Dupa ce fiecare obiect din interfata este conectat functional cu
restul obiectelor,se poate trece la etapa de design.In mediul Visual C#,
obiectele sunt vizibile in etapa de design,astfel incat nu trebuie decat
sa alegeti locatia,dimensiunile,culorile etc.In cazul codurilor editate
manual,se pot utiliza solutii standard,module mai vechi,sau pur si simplu
se vor face cateva tatonari succesive.Nu este recomandabil ca un singur
container sa fie incarcat cu prea multe obiecte,decat atunci cand este
vorba despre o colectie de obiecte de acelasi fel: lista,arie,dictionar.
Este bine ca obiectele legate intre ele prin functionalitate comuna sa
fie incluse in containere diferite,fie ca acestea sunt obiecte vizuale
sau containere simple.Se pot utiliza foldere,ferestre de tip Form sau
obiecte vizuale de tip Panel.Obiectele astfel grupate vor avea un spatiu
de vizibilitate restrans si implicit o siguranta crescuta in executie.
Cu cat memoria este mai fragmentata,cu atat executia va fi mai rapida
si mai sigura.In plus,functionalitatea unui astfel de grup de obiecte va
fi foarte usor de transferat de la un program la altul.

-102-
EXEMPLU:
using System;
using System.Windows.Forms;
using System.Drawing;

public partial class AWindow: System.Windows.Forms.Form{


public AWindow() {
this.Size = new Size(600,400);
Panel panel1 = new Panel();
TextBox textBox1 = new TextBox();
Label label1 = new Label();
RadioButton r1 = new RadioButton();
panel1.Location = new Point(56,72);
panel1.Size = new Size(264,152);
panel1.BorderStyle = BorderStyle.Fixed3D;
label1.Location = new Point(16,16);
label1.Text = "Eticheta pentru TextBox:";
label1.Size = new Size(104,16);
textBox1.Location = new Point(16,32);
textBox1.Text = "...textul implicit";
textBox1.Size = new Size(152,20);
r1.Location = new Point(16,80);
r1.Size = new Size(120,20);
r1.Text = "Butonul radio";
this.Controls.Add(panel1);
panel1.Controls.Add(label1);
panel1.Controls.Add(textBox1);
panel1.Controls.Add(r1);
}
static void Main(){ Application.Run(new AWindow));}
}
Salvati fila cu numele Window12.cs si compilati.Observati ca fiecare
container include obiectele prin metoda Controls.Add().Modulul de mai
sus nu face decat sa grupeze trei obiecte.Pentru a fi complet,cele trei
obiecte trebuie sa fie conectate intre ele prin metode sau evenimente.
Nu este utila nici fragmentarea excesiva a programului.Fie ca se
utilizeaza fire de executie paralele (thread-uri),fie ca se imparte
executia in ferestre si module independente,sau containere,este bine
ca fiecare proces sa execute un calup de memorie proportionat in functie
de memoria de lucru a procesorului.Acest calup este bine sa nu depaseasca
30 % din capacitatea maxima (tamponul intern de memorie) a procesorului.
Concret,fragmentarea nu este utila pentru nici un program mai mic de
1 Mb,dar este indispensabila in cazul aplicatiilor ce lucreaza cu baze
de date,sau cu date preluate si procesate de la adrese de memorie din
retea.In mediu de retea,trebuie avut in vedere si spatiul necesar pentru
protocoalele de comunicatie si obiectele necesare pentru conectare.
Este posibila si o solutie mixta.Proiectati si programati aplicatia
in mediu visual,apoi preluati codurile si compilati programul cu csc.exe,
pentru a obtine un modul unic,portabil,de dimensiuni mici.
Pentru aplicatiile mari,sau comunicatii in retea,este recomandabil sa
utilizati exclusiv solutiile standardizate din mediul Visual C#.

-103-
System.Data

Contine clasele necesare pentru conectarea si preluare de date din


baze de date si tabele.Aceleasi obiecte pot organiza si date create
dinamic,in timpul executiei:
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("Echipe");
DataColumn pozitie = new DataColumn("Pozitie",typeof(int));
DataColumn nume = new DataColumn("Nume");
DataColumn calificare = new DataColumn("Calificare",typeof(bool));
table1.Columns.Add(pozitie);
tabel1.Columns.Add(nume);
tabel1.Columns.Add(calificare);
Info1.Tables.Add(tabel1);
DataRow newRow1;
for( int i = 1; i<4; i++) {
newRow1 = tabel1.NewRow();
newRow1["Pozitie"] = i;
tabel1.Rows.Add(newRow1); }
tabel1.Rows[0]["Nume"] = "Steaua";
tabel1.Rows[1]["Nume"] = "Dinamo";
tabel1.Rows[2]["Nume"] = "Rapid";
tabel1.Rows[0]["Calificare"] = true;
tabel1.Rows[1]["Calificare"] = true;
tabel1.Rows[2]["Calificare"] = false;
Grila1.SetDataBinding(Info1,"Echipe"); }}
Salvati fila cu numele Window13.cs si compilati.Observati cum sunt
interconectate principalele obiecte din interfata.

-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

Contine un set restrans de clase destinate pentru a putea cronometra


operatiile,sau pentru a putea declansa evenimente la anumite intervale de
timp.Dintre acestea,cea mai importanta este clasa Timer (cronometru).Cu
ajutorul aceste clase se construiesc obiecte ce cronometreaza anumite
grupuri de operatii din program.
EXEMPLU:
using System;
using System.Timers;
using System.Windows.Forms;

public class AWindow : Form {


public AWindow(){
System.Timers.Timer Timer1 = new System.Timers.Timer();
Timer1.Enabled = true;
Timer1.Interval = 2000;
Timer1.Elapsed += new System.Timers.EventHandler(Timer1_Elapsed);
}
private void Timer1_Elapsed(object sender,
System.Timers.ElapsedEventArgs e)
{ MessageBox.Show("2 secunde!","Eveniment nou !"); }
static void Main(){ Application.Run(new AWindow()); }
}
Salvati fila cu numele Window16.cs si compilati.In acest exemplu este
necesar sa fie utilizata expresia completa: System.Timers.Timer atunci
cand se construieste obiectul pentru a face distinctia de clasa Timer din
Windows.Forms.Se observa ca la fiecare doua secunde,cronometrul emite un
nou mesaj.Cronometrul este creat pe baza cronometrului intern din procesor
si poate functiona 24 de ore din 24.Pentru a declansa un eveniment se
utilizeza metoda Elapsed conectata la functia de raspuns (delegat).Pentru
ca evenimentul sa poata fi repetat,trebuie ca proprietatea AutoReset sa
fie setata True.Daca este setata False,evenimentul nu se va mai repeta la
urmatorul interval setat.Intervalul se seteaza prin proprietatea Interval.
Cronometrul poate fi controlat prin metodele Start() si Stop(), apoi
poate fi sters din memorie cu Dispose().Alta proprietate este Enabled,
prin care se specifica daca cronometrul poate sa declanseze un eveniment
sau evolueaza doar in background (este inactivat).
Cronometrele sunt concepute pentru a putea evolua in mediu de multi-
procesare cu thread-uri multiple.Fiecare fir de executie (thread) va putea
avea unul sau mai multe cronometre proprii,ce guverneaza toate operatiile
din stream.Prin combinarea unui numar oarecare de thread-uri,cu unul sau
mai multe cronometre, se pot obtine rutine de automatizare complexe,usor
de programat si usor de depanat. Pentru controlul cronometrelor se pot
conecta si cateva obiecte vizuale prin care utilizatorul poate seta
proprietatile,sau poate comanda una dintre metode.In aceste situatii este
utila si proprietatea SynchronizingObject.Pentru a bloca complet executia
unui cronometru cu Stop() si Dispose(),se va seta si SynchronizingObject
la valoarea "null".In caz contrar,evenimentul conectat la cronometru va
putea fi declansat si dupa oprirea cronometrului (daca mesajul de executie
a fost trimis inainte de Stop() si asteapta trecerea intervalului setat).

-108-
System.Runtime

Contine clase esentiale pentru managementul automat al memoriei.Astfel,


CGSettings permite setarea operatiei de "garbage collection" pentru
procesul aflat in executie,iar MemoryFailPoint verifica daca exista
suficienta memorie pentru executia procesului curent.
EXEMPLU:
using System;
using System.Runtime;
using System.Collections;
using System.Windows.Forms;
class Test {
static void Main(){
Queue workQueue = new Queue(50);
for (int x=1; x<30; x++){
try { MemoryFailPoint memFailPoint = new MemoryFailPoint(1000);
for (int n=1; n < 2500000; n++)
workQueue.Enqueue(n);
}
catch (InsufficientMemoryException e)
{ MessageBox.Show("Memorie insuficienta","mesaj");
Console.WriteLine(e);
workQueue = null;
GC.Collect();
e = null; }
Console.WriteLine("Stiva= {0} MB",GC.GetTotalMemory(false) >> 20);
Console.WriteLine("Nr. colectari GC= {0}",GC.CollectionCount(0));
};
workQueue = null;
GC.Collect();
System.Console.ReadLine();
}}
Salvati fila cu numele TestMemorie1.cs si compilati.In exemplul de mai
sus,obiectul memFailPoint va detecta la un moment dat ca memoria de
executie este insuficienta (daca memoria de RAM este mai mica decat 1536
MB).In caz contrar ajustati valoarea din constructor).Mai mult decat atat,
in functia catch (declansata de mesajul de eroare emis) este inclusa si
o operatie de eliberare automata a memoriei,pentru a permite continuarea
facila a executiei.Acest gen de solutii se pot proiecta pentru depanarea
automata a programelor ce lucreaza cu baze de date mari,sau cu resurse
de dimensiune variabila.Este util sa aplicati cateva bucle de acest gen
si in etapa de testare si depanare a unui program,pentru a depista orice
situatie in care memoria de executie atinge o cota critica.Astfel,pentru
executia unei aplicatii vizuale,este recomandabil ca in orice moment al
executiei sa ramana cel putin 200 MB de memorie libera.Pentru aplicatiile
de tip Consola,acest gen de masuri de precautie sunt de obicei inutile.
Calculatoarele au o configuratie hardware extrem de polimorfa.Daca
proiectati o rutina de depanare automata,este bine ca setarea obiectului
de tip MemoryFailPoint sa se faca in functie de valoarea memoriei de RAM
instalate (utilizati si o functie pentru detectia RAM-ului instalat).
MemoryFailPoint se combina eficient cu GC.Collect.

-109-
System.Media

Contine clasele necesare pentru a putea executa un fisier audio de


tip .wav,sau pentru a putea deschide fisierele de sunet ale sistemului.
Include urmatoarele clase: SoundPlayer,SystemSound si SystemSounds.
Clasa SoundPlayer construieste un obiect complex,ce poate fi utilizat
pentru monitorizarea fondului sonor din aplicatie.Principalele metode
sunt destinate pentru a putea incarca si executa un fisier audio,fie
in mod asincron (thread unic), fie sincron (in mediu multithreading).Daca
executia este asincrona,fisierul va fi interpretat fara intrerupere de
la un capat la altul,iar daca este executat sincron, fisierul va fi
impartit in fragmente mici ce vor fi executate concomitent cu alte
operatii sincrone.Daca se combina cu alte obiecte,se poate obtine un
manager de sunet complex,cu mai multe fisiere sonore si mai mai multe
linii de executie.Cel mai simplu SoundPlayer apeleaza metodele Load() si
Play().
EXEMPLU:
using System;
using System.Drawing;
using System.Media;
using System.Windows.Forms;

public class Form1 : Form {


public static void Main() { Application.Run(new Form1()); }
private Button button1;
public Form1(){
button1 = new Button();
button1.Left = 200;
button1.Text = "Play";
button1.Click += new EventHandler(button1_Click);
this.Controls.Add(button1);
}
private void button1_Click(object sender,System.EventArgs e)
{
SoundPlayer player = new SoundPlayer();
player.SoundLocation = "Andante.wav";
player.Load();
player.Play();
MessageBox.Show("Exit ?","");
Application.Exit();
}}
Salvati fila cu numele Sound.cs si compilati.
In continuare,aplicatia se poate dezvolta astfel: se adauga un buton
conectat la un obiect de tip OpenFileDialog pentru a permite alegerea
oricarei file din memorie,se adauga un buton ce controleaza metoda:
Player.Stop,se adauga butoane ce permit executia sincrona sau asincrona,
...etc.
Obiectul SoundPlayer nu contine codec-urile pentru fisierele audio
de alt tip decat WAV (MP3,CD Audio,MPEG sau MIDI...etc).Toate celelalte
fisiere audio vor putea fi deschise si executate doar dupa conversia la
formatul .wav.

-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

Contine clase ce permit o serie de operatii de mententanta,printre


care merita remarcate: monitorizarea performantei sistemului si a
proceselor aflate in executie,monitorizarea conexiunilor active cu alte
aplicatii si resurse din retea (event logs).Dintre aceste clase,trebuie
mentionate: Debug,EventLog,PerformanceCounter,Process,Stopwatch,Switch,
Trace si TraceListener.
O etapa importanta in monitorizarea sistemului de operare o reprezinta
evaluarea proceselor aflate in executie.Pentru acest scop,se poate apela
la clasa Process.
EXEMPLU:
using System;
using System.Diagnostics;
using System.ComponentModel;

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;

public class Form1: Form{


public static void Main() { Application.Run(new Form1()); }
private Button buton1,buton2;
private RichTextBox t1;
private Stopwatch s1;
public Form1(){
buton1 = new Button();
buton1.Left = 120;
buton1.text = "Start";
buton1.Click += new EventHandler(buton1_Click);
buton2 = new Button();
buton2.Left = 210;
buton2.Text = "Stop";
buton2.Click += new EventHandler(buton2_Click);
t1 = new RichTextBox();
s1 = new Stopwatch();
this.Controls.AddRange(new Control[]
{ buton1,buton2,t1} );
}
private void buton1_Click(object sender,System.EventArgs e) {
t1.Clear();
t1.Appendtext("Start cronometru" + "\n");
s1.Start();
}
private void buton2_Click(object sender,System.EventArgs e) {
s1.Stop();
TimeSpan ts = s1.Elapsed;
string interval = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours,ts.Minutes,ts.Seconds,ts.Miliseconds / 10);
t1.AppendText(interval + "\n");
t1.Appendtext("Stop cronometru");
}}
Salvati fila cu numele Cronometru.cs si compilati.Observati ca timpul
scurs intre Start si Stop este salvat in proprietatea Elapsed sub forma
unei date de tip TimeSpan,adica separat in ore,minute secunde si milisec.
Cronometrarea proceselor are rost atat pentru a evalua performanta
sistemului,cat si pentru a stabili intervalele de timp la care urmeaza
sa fie puse in executie procesele automate ("timeing-ul" unor procese).
Deasemenea,cronometrul se poate utiliza si pentru a limita timpul de
acces la o anumita resursa.
-114-
Debug si Trace sunt doua clase concepute special pentru depanarea
codului,sau pentru a urmari parametrii de executie ai unei aplicatii.
Se utilizeaza alternativ iar compilarea trebuie facuta cu optiunile:
d/DEBUG si respectiv d/TRACE.Cele doua clase sunt practic identice si
sunt destul de asemanatoare cu Console.Out,dar adauga si cateva metode
specifice: Assert,Fail,Indent si Unindent precum si un set complet de
metode Write si WriteLine.Practic,cele doua clase se utilizeaza pentru
a scrie output intr-un stream de iesire.In timpul executiei,datele marcate
cu Trace sau Debug vor fi selectate si extrase pentru postanaliza.
EXEMPLU:
using System;
using System.IO;
using System.Diagnostics;

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

In capitolele anterioare au fost prezentate elementele de baza ale


limbajului C# si structura platformei .Net.Notiunile prezentate sunt
suficiente pentru a putea dezvolta programe mici,cu fila unica sau cu
un numar limitat de resurse si module executabile.Pentru a favoriza
dezvoltarea unor aplicatii complexe,la standard profesional,compania
Microsoft a dezvoltat si oferit programatorilor un set de interfete
visuale,cu scopul de a simplifica managementul resurselor,designul visual,
conceptia si depanarea codurilor.Aceste interfete sunt grupate sub numele
de Visual C# Express Edition si au fost publicate in mai multe versiuni,
denumite in functie de anul aparitiei (2005,2008,2010...etc).
O astfel de platforma ofera urmatoarele facilitati:
-un compilator pentru limbajul C#,incorporat (asemanator cu csc.exe)
-un Help visual interactiv,denumit IntelliSense
-o fereastra speciala pentru output de tip Debugg si Trace
-un mediu vizual de design pentru obiectele de tip Windows Forms
-suport logistic pentru bibliotecile Windows Presentation Foundation (WPF)
-suport logistic pentru aplicatiile ce contin baze de date locale
-limbaj integrat pentru interogari (LINQ)
-acces la serviciile de tip Windows Communication Foundation (WCF)
Platforma contine mai multe interfete,specializate pentru un anumit
tip de aplicatie.Cu ajutorul lor,se pot creea:
-aplicatii Windows cu ferestre de tip Form
-aplicatii Windows de tip Window Presentation Foundation
-aplicatii de tip Console (similare cu cele prezentate anterior)
-biblioteci de clase si biblioteci DLL
-aplicatii pentru baze de date ce utilizeaza serverul SQL
-jocuri PC bazate pe tehnologia DirectX
Mai mult decat atat,platforma vizuala nu numai ca organizeaza automat
toate filele de resurse, dar ofera si un schelet de cod,in care nu mai
trebuie decat sa completati expresiile ce formeaza aplicatia.In acest mod,
programele vor avea o structura standardizata,foarte usor de urmarit si
depanat,chiar daca programul a fost editat de un alt programator.Daca se
respecta scheletul construit automat,programele vor putea fi depanate mai
usor si utilizand programe de verificare automatizate.
Toate aceste facilitati,grupate la un loc,formeaza mediul software in
care urmeaza sa fie dezvoltat programul si poarta numele de Integrated
Development Enviroment (IDE).Principalele instrumente de lucru din IDE
sunt:
-Code Editor : este fereastra in care se editeaza codurile
-Windows Form Designer : este instrumentul pentru design visual
-Data Explorer : permite vizualizarea datelor din bazele de date
-Debugger: afiseaza erorile depistate de compilator
-Toolbars: sunt barele pentru instrumente visuale
-Project Node: prezinta arborele binar al filelor de resurse
-Class View: afiseaza proiectul sub forma de clase si metode
-Output window: afiseaza informatiile generate in timpul constructiei
-Properties window: afiseaza proprietatile obiectelor
-Toolbox window: contine obiectele pentru design
-Task List: afiseaza lista de sarcini aflate pe rol

-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

Termenul de "visual" aplicat platformei Microsoft Visual C sharp,


provine de la fereastra de design in care toate obiectele visuale pot fi
previzualizate inca din etapa de editare a programului.In aceasta ferastra
se va proiecta aspectul interfetei grafice cu utilizatorul.Principalul
avantaj il reprezinta faptul ca obiectele se pot redimeniona sau pot fi
repozitionate dupa bunul plac,cu ajutorul indicatorului mouse.Nu mai este
necesar sa recompilati codul progresiv,de zeci de ori,pentru a obtine
rezultatul scontat.Pur si simplu,asezati obiectele pe panoul de lucru si
apoi alegeti culorile,dimensiunile,pozitia,grupurile de obiecte...etc.
Inca din etapa de design,obiectele cu care lucrati sunt obiectele
reale din program,cu proprietati,metode si evenimente.Pentru a observa
toate amanuntele unui obiect,se poate utiliza fereastra Properties.Daca
aveti un minimum de experienta cu orice alta platforma visuala (Delphi,
C,Visual Basic etc.),interfata grafica din C# va fi extrem de usor de
exploatat.In caz contrar,este recomandabil sa utilizati un tutorial din
reteaua Internet.Acest manual nu va prezenta exhaustiv toate operatiile
si tastele short-cut,sau strategiile de design grafic,ci va prezenta doar
cateva solutii simpliste,exemplificative.
Frecvent,incepatorii deschid platforma de lucru,inainte de a sti exact
ce anume doresc sa faca.Apoi se orienteaza "vazand si facand",pana cind
programul este cat de cat functional.Pentru a realiza programe bune,este
esential ca munca sa inceapa fara calculator.In prima etapa,trebuie sa
formulati clar premiza programului: ce anume doriti sa faca.In etapa
urmatoare,este recomandabil sa va imaginati cum anume doriti sa fie
interfata cu utilizatorul: cu butoane,cu casete de dialog,cu diagrame si
grafice interactive,prezentare video sau audio...etc.Apoi este bine sa
desenati pe hartie schema de baza,cu obiectele si pozitia lor relativa.In
cazul programelor complexe,este util sa desenati si o diagrama de flux,in
care sa proiectati ordinea de executie a principalelor seturi de operatii.
Diagramele nu trebuie sa contina amanunte si solutii tehnice,ci doar
schema de principiu.Solutiile se vor dezvolta ulterior,in functie de
complexitatea mediului de executie.In etapa urmatoare,studiati atent toate
resursele oferite de platforma .Net si alegeti cu atentie obiectele pe
care doriti sa le exploatati,apoi alegeti metodele si proprietatile cu
care doriti sa implementati functionalitatea programului.De cele mai multe
ori,exista doua sau mai multe obiecte asemanatoare.Exemplu: pentru a stoca
temporar seturi de date se pot utiliza: arii de date,liste,dictionare,
stive,colectii,stream-uri etc.Alegerea se va face atat in functie de
experienta d-voastra anterioara,cat si in functie de mediul de executie.
Pe cat posibil,se va alege solutia ce mai simpla,cu minimum de operatii
si cu minimum de consum de memorie.Daca aveti o solutie prefabricata din
alt program,va fi intotdeauna usor de adaptat,sau de reutilizat.Este bine
sa utilizati obiectele cu care sunteti deja familiarizati si solutiile
tehnice pe care le stapaniti cel mai bine.In momentul in care deschideti
platforma de lucru,trebuie sa stiti exact ce anume doriti sa faceti si
cum.Daca programul va avea nevoie si de resurse software externe,este
indicat sa organizati toate aceste resurse inainte de a incepe editarea
programului.La nevoie,este bine sa va consultati si cu persoane ceva mai
experimentate,inainte de a incepe munca propriu zisa.

-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

O aplicatie Windows poate include si un fundal sonor.Fisierele audio,


pot contine explicatii si recomandari pentru utilizare,sau pur si simplu
pot adauga un fundal muzical.Pentru a nu incomoda munca utilizatorului,
este recomandabil ca toate operatiile sa se execute automat.Sa presupunem
ca doriti sa adaugati un set de melodii ce vor fi interpretate aleator.
Pentru organizarea melodiilor se va utiliza un folder special,denumit
Melo,in care se arhiveaza fisiere de tip .wav.Redarea fisierelor se va
face cu un obiect de tip SoundPlayer,iar bucla de repetitie se va putea
structura cu ajutorul unui obiect de tip Timer.Controlul se va asigura cu
ajutorul unui buton simplu,la care se pot adauga si doua etichete pentru
a afisa melodia si durata.
Deschideti un proiect nou si adaugati in fereastra Form1 un buton si
doua etichete.Modificati textul butonului in "Play",textul primei etichete
(label) in "Melodia" iar textul celei de a doua etichete in "Durata".
Apasati tasta F5 pentru a compila rapid proiectul,apoi alegeti din meniul
File optiunea Close Solution si salvati proiectul in C:\Aplicatii cu
numele MusicPlayer.In acest moment se pot adauga resursele locale.La
adresa C:\Aplicatii\MusicPlayer\MusicPlayer\bin\Debug adaugati un folder
denumit Melo in care arhivati una sau mai multe fisiere cu extensia .wav.
In acest moment,proiectul contine toate elementele necesare.Se poate
trece la editarea codului.Redeschideti proiectul cu File/Open Project,
si adaugati un obiect de tip Timer.Executati un dublu click pe buton si
modificati fila Form1.cs astfel;
adaugati: using System.IO;
using System.Media;
using System.Timers;
editati metoda button1_Click() astfel:
timer1.Start();
this.PlayMusic();
adaugati metoda PlayMusic astfel:

public void PlayMusic() {


SoundPlayer player = new SoundPlayer();
DirectoryInfo d = new DirectoryInfo("Melo");
FileInfo[] fis = d.GetFiles();
int status = fis.Length;
Random numar = new Random();
int selectie = numar.Next(status);
player.SoundLocation = "Melo\\" + fis[selectie].Name;
int timp = Convert.ToInt32(fis[selectie].Length/100);
timp = timp * 55;
timp = Convert.ToInt32(timp/100);
timp = Convert.ToInt32(timp + timp / 60 + 5000);
timer1.Interval = timp;
label1.Text = fis[selectie].Name;
label2.Text = Convert.ToString(timp / 1000) + " secunde";
player.Load();
player.Play();
player.Dispose();

-125-
d = null;
fis = null;
numar = null;
GC.Collect();
}
in final editati metoda timer_Tick astfel:

private void timer1_Tick(object sender, EventArgs e)


{ this.PlayMusic() }
Daca fereastra de debugg nu contine erori,compilati si executati
apasand tasta F5.
Analizand putin exemplul,se observa urmatoarele operatii:
-la apasarea butonului se porneste timerul
-la fiecare tick,se construieste un obiect nou de tip SoundPlayer
-se citesc toate filele din directorul Melo si se alege una,aleator
-durata melodiei se calculeaza aproximativ,utilizand lungimea fisierului
( aproximativ 10 Mb / minut in cazul fisierelor .wav)
-se afiseaza numele melodiei si durata
-se seteaza intervalul timerului egal cu durata melodiei alese
-se pune in executie obiectul SoundPlayer
-se elibereaza complet memoria
-se forteaza GarbageCollector

In cazul fisierelor .wav,o melodie poate ocupa zeci de Mb.Din acest


motiv,este esential ca memoria sa fie complet eliberata dupa fiecare
melodie.Daca doriti ca fisierele sa fie executate intr-o anumita ordine
fixa,algoritmul aleator se va inlocui cu o lista simpla de unde vor fi
citite succesiv.
Aceasta aplicatie nu contine decat o functionalitate rudimentara.Am
eliminat intentionat toate artificiile de design, pentru a reduce la
maximum consumul de memorie.Modulul executabil nu consuma mai mult de
9 Kb.Acest gen de modul,poate fi incorporat intr-o aplicatie mai mare,
sub forma de functionalitate optionala.Pentru a crea un MusicPlaer complet
trebuie adaugata si o interfata grafica in care utilizatorul sa poata
alege si grupa melodiile dupa bunul plac,cu cateva click-uri de mouse.
Optional se pot crea link-uri spre diverse adrese de retea,de unde se pot
descarca automat melodiile dorite, sau se pot conecta doua sau mai multe
astfel de module,pentru a realiza mixaje sonore.
Nu este obligatoriu ca fisierele sa contina muzica.Pot fi mesaje si
indicatii,sunete optionale sau semnale de avertizare...etc.
Obiectul SoundPlayer nu este destinat pentru aplicatii complexe de tip
MediaPlayer ci doar pentru efecte sonore simple,sau pentru redarea unor
fisiere audio in format .wav.Obiectul nu recunoaste si nici nu decodeaza
nici unul dintre fisierele audio cu compresie (.Mp3,.midi etc.).Pentru
acest gen de situatii,se poate apela direct la Windows MediaPlayer (vezi
si exemplul urmator).
Complexitatea solutiei se va alege in functie de ponderea pe care o
ocupa in program operatiile de tip audio.Daca fundalul sonor este doar
optional,se va alege solutia cea mai simpla si economica,iar daca este o
aplicatie specializata pentru fisierele audio se va prefera un obiect mai
complex (dintre cele aflate in lista Choose Toolbox Items).
-126-
Exemplu MediaPlayer

In interfata grafica sunt incluse toate componentele visuale utilizate


in mod curent pentru design.Paleta de componente nu epuizeaza insa toate
resursele ce pot fi exploatate in proiect.Atunci cand este necesar,paleta
de componente poate fi completata cu orice control de tip ActiveX instalat
in calculator.Paleta de controale ActiveX depinde de programele instalate
in calculatorul d-voastra si poate fi activata alegand din meniul Tools,
optiunea Choose Toolbox Items.
Un exemplu concret il reprezinta aplicatiile audio, pentru file cu un
format oarecare de compresie.Pentru a deschide si executa astfel de file
se poate apela la controlul implicit din mediul Windows denumit Windows
MediaPlayer.Cu un asfel de control,se poate crea o aplicatie similara cu
cea precedenta,dar capabila sa deschida orice format audio.Proiectul va
include un buton,un obiect MediaPlayer si cateva etichete Label.Fisierele
se vor organiza intr-un folder,denumit Melo,la fel ca si in exemplul
anterior.
EXEMPLU: Deschideti un proiect nou si adaugati un buton,apoi salvati
proiectul in C:\Aplicatii pentru a avea acces la resurse.Adaugati in
directorul Debug un folder denumit Melo,in care puteti arhiva mai multe
fisiere audio cu format diferit: mp3,midi,wav,avi ...etc.
Redeschideti proiectul.Pentru a adauga un control MediaPlayer alegeti
din Tools optiunea Choose Toolbox Items si selectati COM Components,apoi
bifati componentul Windows Media Player si confirmati cu OK.Noul control
va apare in Toolbox in grupul Components (ultimul).Adaugati in fereastra
Form1 un control Media Player si 5 etichete Label.Redimensionati toate
obiectele pana obtineti aspectul dorit,modificati textul pentru buton in
Play sau Start si pentru cele 5 etichete in: Adresa,Status,Autor,Titlul
si Albumul.Adaugati si un control de tip Timer.Apoi executati un dublu
click pe buton si modificati codul astfel:
adaugati using System.IO;
editati urmatoarea metoda:
public void PlayMusic() {
DirectoryInfo d = new DirectoryInfo("Melo");
FileInfo[] fis = d.GetFiles();
int status = fis.Length;
Random numar = new Random();
int selectie = numar.Next(status);
label1.Text = "Melo\\"+fis[selectie].Name;
axWindowsMediaPlayer1.URL = label1.Text;
label2.Text =
axWindowsMediaPlayer1.currentMedia.sourceURL.Length.ToString();
label3.Text = axWindowsMediaPlayer1.currentMedia.getItemInfo("Artist");
label4.Text = axWindowsMediaPlayer1.currentMedia.getItemInfo("Title");
label5.Text = axWindowsMediaPlayer1.currentMedia.getItemInfo("Album");
axWindowsMediaPlayer1.Ctlcontrols.play();
d = null;
fis = null;
numar = null;
GC.Collect();
}
-127-
modificati metoda pentru buton astfel:
private void button1_Click(object sender,EventsArgs e) {
PlayMusic();
timer1.Start();
}
apoi adaugati metoda timer1_Tick astfel:
private void timer1_Tick(object sender,Events Args e) {
if ( axWindowsMediaPlayer1.status == "Stopped")
PlayMusic();
label2.Text = axWindowsMediaPlayer1.status;
}
Verificati fereastra View Error List si corectati eventualele erori,apoi
compilati si testati aplicatia apasand tasta F5.
In final,puteti adauga un icon personalizat sau puteti modifica
Fila AssembleyInfo.cs pentru a schimba fila manfiest.Este bine sa testati
cu atentie modul de executie si memoria consumata.Pentru acest scop,puteti
lansa programul in executie si apoi deschideti utilitarul Task Manager
si urmariti memoria consumata si randamentul de procesare.
Obiectul Windows Media Player este mult mai complex decat obiectul
SoundPlayer,dar fiind un obiect de tip ActiveX extern,nu va fi afisat in
fereastra Properties decat cu un set minimal de proprietati.Pentru a
putea exploata intregul set de metode si proprietati trebuie sa studiati
cu atentie documentatia tehnica a controlului,sau puteti sa editati in
fereastra de cod numele obiectului (axWindowsMediaPlayer1) si apoi sa
utilizati wizardul IntelliSense.
Atunci cand proiectul apeleaza la resurse externe,apar o serie intreaga
de probleme de portabilitate.Controlul Windows Media Player este integrat
in mediul Windows (sub o versiune sau alta) asa ca va putea fi apelat la
orice utilizator de Windows.Totusi,pachetul ce contine programul va trebui
sa includa si filele AxInterop.WMPlib.dll,respectiv Interop.WMPLib.dll
(generate automat in directorul Debug).
In situatiile in care utilizati controale ActiveX ce nu sunt incluse in
sistemul de operare,va trebui sa adaugati in pachet si controlul ActiveX,
impreuna cu solutia de instalare in calculatorul gazda si cu un set
minimal de informati explicative.
In general, aplicatiile dezvoltate in mediul Visual C sharp pot contine
nenumarate tipuri de resurse,setate pentru diverse tipuri de configuratii
hardware.Pentru orice program in care se includ si solutii diferite de
cele implicite,este bine ca programul sa includa si o fila in care sunt
specificate sub forma de lista toate resursele hardware si software
necesare pentru proiect,precum si explicatii detaliate de instalare.
In exemplul de mai sus,gestiunea resurselor locale se face extrem de
simplu.Pur si simplu se trag filele dorite in directorul Melo.Atunci cand
se lucreaza cu resurse mai complexe,acest gen de operatii nu pot fi facute
simplist si este necesar sa adaugati si o aplicatie auxiliara cu ajutorul
careia utilizatorul sa poata sa-si organizeze resursele.Exemplu: daca
resursele sunt gestionate intr-o baza de date,utilizatorul trebuie sa
primeasca si o interfata grafica prin care sa poata adauga sau elimina
fisiere din arhiva.Facultativ se pot adauga si masuri de securizare a
datelor prin parole de acces,conturi de utilizator,criptare si decriptare
a datelor,semnaturi digitale...etc.
-128-
Exemplu ChartFX

In majoritatea lucrarilor stiintifice,apare si necesitatea de a putea


prezenta serii de date,sub forma de histograme,diagrame,grafice etc.
Exista un set intreg de solutii posibile.Cea mai simpla solutie este sa
desenati diagramele cu Paint si sa le prezentati sub forma de imagini
simple.Totusi realizarea unui astfel de desen este destul de laborioasa
si consuma mult timp.Pentru astfel de operatii,exista un control ActiveX
specializat,denumit ChartFX,ce poate fi importat in interfata grafica la
fel ca si Windows Media Player,din Tools/Choose Toolbox Items.
Deschideti un proiect nou si adaugati ChartFx in lista de componente.
(vezi exemplul precedent).Alegeti un astfel de obiect si apoi modificati
dimensiunile dupa bunul plac.Obiectul va fi initializat cu un set de
valori atribuite aleator.Pentru a putea obtine graficul dorit,executati
un click drept pe obiect si alegeti Properties.Se va deschide fereastra
Properties cu un set de meniuri.Alegeti Tools si apoi bifati casetele
ToolBar si Legend iar apoi confirmati cu Apply si OK. Restul de meniuri se
pot utiliza pentru a personaliza aspectul. In meniul Data Values alegeti
cate serii de valori doriti sa prezentati si cate valori vor fi in fiecare
serie, iar in meniul appearance alegeti stilul de prezentare.
Redimensionati obiectul astfel incat sa fie vizibila intreaga bara de
instrumente (ToolBar),apoi puteti sa previzualizati datele apasand tasta
F5.Dupa compilare,fereastra Form1 va fi activa,iar bara de instrumente va
fi functionala. Pentru a putea introduce valorile dorite,selectati butonul
Tools (cel cu un ciocan) si bifati optiunea Data Editor. In fereastra se
va afisa o grila,in care puteti introduce seriile de valori dorite.
Pentru a seta valorile introduse, apasati butonul Copy to clipboard
(cel cu un aparat de fotografiat), apoi alegeti din nou butonul Tools si
deselectati optiunea Data Editor.
In acest moment, obiectul va afisa datele introduse de d-voasta.
Cu butoanele Area Chart, Pie Chart ...etc puteti sa schimbati forma de
prezentare,iar cu butonul 3D puteti opta intre graficele 2D sau 3D.Cu
butonul Rotate the chart puteti schimba unghiul de incidenta al coloanelor
3D iar cu butonul Edit Titles puteti adauga titlurile explicative.Optional
pueti adauga si grile orizontale,verticale sau mixte. Cand graficul are
aspectul dorit,salvati fila la adresa dorita,utilizand butonul Export.
In final,salvati aplicatia la adresa dorita. Fila salvata va putea fi
reincarcata ori de cate ori doriti, fie cu butonul Import (primul) fie
cu ajutorul unei comenzi.
Exemplu: salvati fila dorita in directorul Debug cu numele Grafic1.CHF
adaugati un buton
editati metoda clic pentru buton astfel:
private void Button1_Click(object sender,EventArgs e) {
axChartFx1.ImportFile("Grafic1.CHF");
}
Recompilati cu F5.La executie,graficul se va putea incarca apasand pe
buton.Pentru o prezentare mai complexa,un set de grafice se poate incarca
intr-o lista de derulare,la fel ca la albumul foto.Eventual,adaugati un
timer care schimba graficele automat,simultan cu un set de texte
explicative,sau adaugati si un fundal sonor in care prezentati toate
informatiile necesare.

-129-
Exemplu TextToHTML Converter

Un alt gen de aplicatii simple,sunt cele ce implica opratii de analiza


si procesare a unui text,sau de reformatare.Sa presupunem ca doriti sa
includeti intr-un site Web,un set de texte mai vechi,editate cu Notepad.
Pentru a putea converti fila text in fisier hipertext,trebuie adaugat un
set de tag-uri,pentru fiecare rand de text.Sunt necesare un obiect de tip
OpenFile,pentru a deschide fila text si o functie simpla prin care se
face reformatarea textului.
Deschideti un proiect nou, adaugati un buton, schimbati eticheta
butonului in Open sau Conversie sau ceva similar,apoi adaugati si un
obiect de tip OpenFileDialog.
Executati un dublu click pe buton si editati metoda button1_Click
astfel:
openFileDialog1.ShowDialog;
Pentru a putea executa operatii I/O adaugati in lista de import si
using System.IO;
Apoi selectati obiectul OpenFileDialog1 si executati un dublu
click pe evenimentul FileOk.Completati metoda FileOk astfel:

private void openFileDialog1_FileOk(object sender,CancelEventArgs e) {


string path = @openFileDialog1.FileName;
string[] readText = File.ReadAllLines(path);
string[] arie2 = path.Split(new Char[] { '\\' });
int nr = arie.Length;
string nume = arie2[nr - 1];
string adresa = @"C:\\Aplicatii\\Arhiva\\" + nume + ".html";
using (StreamWriter sw = File.AppendText(adresa))
{
sw.WriteLine("<html>");
sw.WriteLine("<body>");
foreach (string sir in readText)
{
sw.WriteLine("<p>" + sir + "</p>");
}
sw.WriteLine("</body>");
sw.WriteLine("</html>");
sw.Dispose();
}
MessageBox.Show("Fila a fost reformatata !");
}
In exemplul de mai sus,am ales sa arhivez filele convertite intr-un
director fix,denumit Arhiva,inclus in C:\Aplicatii.Pentru acest scop am
extras numele filei din adresa filei text (cu Split) si apoi am modificat
extensia.Pentru fiecare sir din text am adaugat un tag.Daca doriti sa
salvati filele la alta adresa, modificati expresia pentru sirul: adresa.
Adaugati folderul pengtru arhivare (Arhiva) si apoi compilati cu F5.
In mod similar,puteti procesa textul pentru a extrage doar elementele
semnificative,pentru a adauga date noi,pentru a introduce titluri si
imagini digitale,sau link-uri active spre alte file,sau orice alt tip de
solutii pentru procesarea automata a textului.

-130-
Exemplu Web Browser

Functionalitatea unei aplicatii poate fi extinsa,prin intergrarea unui


modul pentru exploatarea documentelor de tip Web.Fie ca utilizatorul va
primi date preluate dintr-un site web,fie ca va putea sa navigheze singur,
conexiunea la retea se va face prin intermediul unui control de tip Web
Browser,ce poate fi instalat cu Choose Toolbox Items din paleta COM.
Deschideti un proiect nou si adaugati in Form1 un obiect de tip Web
Browser.Redimensionati fereastra si obiectul astfel incat sa ofere un
spatiu cat mai generos pentru documentele Web.Daca doriti sa afisati un
anumit document,de la o adresa fixa,este suficient sa adaugati urmatoarea
comanda in constructorul ferestrei:
axWebBrowser1.Navigate("http://yahoo.com");
Tastati F5 si verificati adresa.In mod normal,se va afisa automat
documentul Yahoo.In mod similar,se pot adauga cateva butoane ce deschid
automat un set oarecare de adrese.Pentru a crea un modul interactiv,se
poate adauga un control de tip Textbox in care utilizatorul introduce
adresa,si un buton care navigheaza cu o comanda de genul:
axWebBrowser1.Navigate(textBox1.Text);
Exista si situatii in care doriti sa abilitati utilizatorul pentru a
cauta o anumita adresa,in functie de un set de cuvinte cheie.In acest caz,
este recomandabil sa utilizati motoarele de cautare traditionale: Yahoo,
MSDN sau Google.Pentru acest scop,in expresia de navigare se va adauga si
una dintre urmatoarele formule:
"http://search.yahoo.com/search?ei=utf-8&fr=ieas-tb&p=" + textBox1.Text
"http://www.bing.com/search?q=" + textBox1.Text
"http://www.google.ro/search?hl=ro&source=hp&q" + textBox1.Text
Pentru a obtine formulele de search din serverul dorit,puteti utiliza
un browser clasic,in care urmariti modul in care formuleaza adresele la
o comanda de search (formulele se schimba periodic).
Pentru a tine evidenta adreselor deschise si a putea reveni la una
dintre adrese,puteti adauga si un control de tip ListBox,apoi setati
evenimentul NavigateComplete2 pentru WebBrower astfel:
listBox1.Items.Add(axWebBrowser1.LocationURL);
si respectiv evenimentul listBox1_Click astfel:
axWebBrowser1.Navigate(listBox1.SelectedItem.ToString());
Este util sa adaugati si un buton Stop cu evenimentul Click:
axWebBrowser1.Stop();
pentru a putea opri executia atunci cand una dintre adrese nu functioneaza
corect.Verificati erorile si apoi compilati cu F5.
Nu are rost sa proiectati o aplicatie noua de tip Web Browser.Exista
deja sute de astfel de module,gata consacrate.In schimb,puteti integra
functionalitatea unui web browser in aplicatiile de tip client.
Exemplu: deschideti un site web in care actualizati permanet un set de
date sau informatii,si oferiti clientilor o aplicatie prin care pot
accesa automat acel site,sau pot prelucra seturile de date intr-un anumit
fel: aplicatii economice,calcule,date statistice etc.Pentru a prelua
datele din fila deschisa se va utiliza proprietatea Document.
Resursele externe de tip web document,permit ca acelasi set de date sa
poata fi exploatat de un numar foarte mare de clienti,reducand astfel
consumul de memorie si numarul de operatii procesate/informatie obtinuta.

-131-
Exemplu cu baza de date (Biblioteca)

Volumele mari de date,nu pot fi organizate si sistematizate in file de


resurse de tip text,ci necesita un sistem complex de tabele si formule de
asociere,selectie,sau filtrare a datelor,dupa algoritmi prestabiliti.
Acest gen de solutii,este oferit de bazele de date preformatate,combinate
cu interogari de tip SQL.Exista numeroase programe specializate pentru
munca cu baze de date.Platforma Visual C# ofera doar o interfata grafica
pentru exploatarea resurselor de acest gen,create cu oricare dintre
instrumentele Microsoft.O astfel de interfata,combina un set de obiecte
visuale,cu functii diferite.O parte dintre obiecte realizeaza conexiunea
cu baza de date,altele permit formularea expresiilor de selectie si apoi
restructurarea datelor,in timp ce alte obiecte sunt specializate doar in
prezentarea si afisarea sau imprimarea datelor.O solutie tipica,combina
trei sau mai multe astfel de obiecte,ce trebuie conectate intre ele cu
ajutorul unor proprietati specifice.Nu exista o singura solutie,ci se pot
imagina nenumarate tipuri de solutii,in functie de necesitatile de moment.
Sa presupunem ca dorim sa organizam cartile din biblioteca personala,
in asa fel incat sa putem selecta cartile dupa autor,editura,colectie sau
anul aparitiei etc.Vom avea nevoie de o baza de date cu un tabel,de un
obiect pentru prezentarea datelor si de cateva obiecte pentru conexiune
si selectie.
Primul obiectiv este baza de date.Puteti utiliza o baza de date
preexistenta,sau creati una noua.Puteti utiliza oricare dintre programele
sau instrumentele specializate oferite de firma Microsoft: Visual Basic,
SQL Server etc.Creati o baza de date denumita Biblioteca,apoi un tabel
intitulat Carti,cu urmatoarele coloane: Autor,Titlul,Anul,Tematica,
Editura,Colectia,Raftul si Note.Daca nu stiti sa creati tabele,utilizati
un program tutorial,sau un wizard.Toate coloanele pot fi de tip string.
In caz ca doriti sa puteti aplica si operatii matematice asupra unora
dintre date,puteti alege si tipurile numerice.Este important ca largimea
fiecarei coloane sa fie suficienta pentru datele inregistrate.
Exemplu: Anul necesita doar 4 caractere in timp ce pentru Titlul este bine
sa rezervati cel putin 40.Este esential sa structurati corect tabelul.Se
pot face operatii de restructurare si dupa introducerea datelor,dar acest
gen de operatii sunt nerecomandabile (baza de date pastreaza sablonul
initial si trebuie sa refromateze tabelul la fiecare actualizare).Dupa ce
tabelul are structura dorita,este bine sa initializati baza de date si
sa introduceti primele date (cateva carti).
Inainte de a incepe designul grafic este bine sa verificati baza de
date si conexiunea.Lansati programul Visual C#,apoi alageti din meniul
Tools optiunea Connect to Database.Se va deschide fereastra de control
Add Connection.Navigati cu ajutorul butonului Browse si selectati baza
de date dorita.Eventual apasati si butonul Test Connection si finalizati
wizard-ul.In continuare,pentru a putea evalua conexiunea,alegeti din
meniul View,optiunea Other Windows si apoi Database Explorer.In fereastra
Database Explorer,executati un click pe semnul plus de langa numele bazei
de date,pentru a deschide directoarele: Tables,Views,Stored Procedures si
Functions in care gasiti toate informatiile utile despre baza de date.
In cazul exemplului de mai sus,in Tables trebuie sa apara tabelul Carti,
in care regasiti numele coloanelor (apasand pe semnul plus).

-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

Sistemul de operare monitorizeaza in permanenta principalele date cu


privire la procesele aflate in executie,timpul de executie per operatie,
timpul de intarziere pe lista de asteptare,consumul de memorie etc.Clasa
PerformanceCounter permite accesul direct la aceste date.Proprietatile:
CaregoryName,CounterName si InstanceName permit conectarea obiectului la
datele arhivate de sistemul de operare in Registery.Citind aceste date
in mod dinamic,se poate monitoriza performanta sistemului de operare, a
procesorului sau a unei aplicatii oarecare.
EXEMPLU: -deschideti un proiect nou si adaugati in fereastra Form1 un
buton si trei etichete Label.Sa presupunem ca doriti sa urmariti in mod
dinamic trei parametri diferiti,ce caracterizeaza: un process oarecare,
procesorul instalat si sistemul de operare.Adaugati un obiect de tip
Timer si apoi editati metoda Click pentru buton astfel:
timer1.Start();
In fereastra Propertis puteti seta intervalul de timp la care doriti
sa se actualizeze datele.Pentru un interval de o secunda,setati Interval
la valoarea 1000 (pentru obiectul timer1).
In continuare,cele trei etichete vor fi conectate la registri prin
trei obiecte de tip PerformanceCounder.Adaugati cele trei obiecte si
setati in fereastra Propertis cele trei proprietati astfel:
pentru performanceCounter1:
CategoryName = Process
CounterName = Elapsed Time
InstanceName = explorer
Acest obiect va monitoriza timpul de executie pentru Internet Explorer.
pentru performanceCounter2:
CategoryName = Processor
CounterName = % Idle Time
InstanceName = _Total
Acest obiect va monitoriza timpul pe lista de asteptare din procesor.
pentru performanceCounter3:
CategoryName = System
CounterName = File Data Operations/sec
InstanceName va ramane nesetat
Apoi selectati obiectul timer1 si editati evenimentul Tick astfel:
private void timer1_Tick(object sender,EventArgs e) {
label1.Text="Timp=" + performanceCounter1.NextValue().ToString();
label2.Text="Idle=" + performanceCounter1.NextValue().ToString();
label3.Text="Nr.operatii=" + performanceCounter3.NextValue.ToString();
}
In final,compilati si executati proiectul cu F5.
Pentru a verifica aplicatia,lansati in executie Internet Explorer,
apoi executati prpiectul si apasati pe buton.Prima eticheta va afisa
timpul de executie,cea de a doua timpul de asteptare in procesor,iar cea
de a treia va afisa numarul de operatii executate de sistem.Eventual
deschideti cateva file, sau descarcati ceva din retea si urmariti cum
este monitorizata executia.Studiati lista de optiuni a celor trei
proprietati, pentru a putea monitoriza alte date dnamice din registrii
sistemului de operare.

-134-
Windows Presentation Foundation

Platforma Microsoft Visual C# 2008 permite dezvoltarea interfetelor


grafice si cu ajutorul tehnologiei denumita generic Windows Presentation
Foundation.Dupa cum spune si numele,aceasta tehnologie este dezvoltata
special pentru a facilita prezentarea datelor sub forma de videoconferinte
videoclipuri,diaporame etc.Principalele facilitati suplimentare sunt date
de capacitatea de a implementa grafica de tip 3D,animatie sau efecte de
tip video,cu minimum de consum de memorie.Interfata grafica este formata
tot din obiecte visuale din platforma .Net Framework,incluse in doua
biblioteci speciale: System.Windows si System.Windows.Controls, dar
definitia si setarea acestor controale nu se face prin linii de cod de
tip C#,ci cu ajutorul unor tag-uri speciale editate in limbaj XAML.
XAML (Extensible Application Markup Language) este o extensie a limbajului
XML ce se caracterizeaza prin separarea codurilor destinate pentru
designul interfetei,de codurile prin care se realizeaza functionalitatea
aplicatiei.Astfel,declararea si setarea caracteristicilor pentru toate
obiectele visuale se va face in limbaj XAML,iar functionalitatea acestor
obiecte se va codifica separat,intr-o fila de tip C# conectata printr-un
link la interfata grafica.Prin acest mecanism,este posibil sa pastrati
aceeasi interfata grafica, dar sa actualizati permanent codurile pentru
executie, sau sa oferiti clientilor aceeasi interfata grafica, dar cu
functionalitati diferite (dupa categoria clientului).
Pentru a putea edita astfel de interfete,este recomandabil sa studiati
si documentatia tehnica (specificatia) limbajului XAML,ce poate fi
obtinuta de la firma Microsoft sub forma documentelor: MS-XAML-2009.pdf
si MS-WPFXV-2010.pdf.In plus,exista o serie intreaga de tutoriale si/sau
manuale gratuite.Este bine sa nu va lansati la design decat daca aveti si
cunostiinte minime despre limbajul XML.In caz contrar,puteti dezvolta
aplicatii de tip WPF,doar daca utilizati solutii standard,copiate din
surse sigure.
Pentru o prima familiarizare cu acest stil de lucru,deschideti
platforma Visual C# si alegeti un proiect nou,de tip WPF Application.
Aplicatia va deschide automat filele: Window1.xaml si Window1.xaml.cs,
adica fila pentru design grafic si fila pentru codurile C#.Daca nu sunt
deschise,deschideti ferestrele Solution Explorer,Properties si Toolbox.
Din fereastra Toolbox,trageti in fereastra Window1 un obiect de tip Label,
setati dimensiunea dorita,apoi alegeti fereastra Properties si editati
in Content textul dorit: Prima aplicatie WPF.Pentru a observa diferenta
fata de un obiect Label Windows Forms,alegeti proprietatea Background si
alegeti o culoare.Exemplu: Gold.
Observati putin fila Window1.xaml.Obiectul Label adaugat este declarat
si setat intr-un singur Tag: <Label ..... ></Label>
Pentru a vedea aspectul grafic,compilati si executati cu tasta F5.
In continuare se poate adauga un buton.Observati cum se modifica linia
de cod,in timp ce redimensionati butonul.Setati proprietatea Content = OK.
Pentru ca butonul sa fie functional,executati un dublu click pe buton si
apoi completati in fila Window.xaml.cs metoda button1_Click() astfel:
MessageBox.Show("Hello,Windows Presentation Foundation!");
Compilati si executati cu tasta F5.Salvati proiectul si apoi puteti face
o scurta comparatie cu acelasi proiect scris in stil Windows Forms.

-135-
Exemple Grafica 2D

Desenele,graficele si desenele animate pot contribui substantial pentru


ilustrarea unei conferinte,sau pentru prezentarea unor date stiintifice.In
majoritatea limbajelor de programare,grafica este corelata cu dispozitivul
grafic instalat.Din acest motiv,sistemul de coordonate grafice va putea
fi diferit de la un utilizator la altul,in functie de placa video sau de
sistemul de operare instalat...etc.In Windows Presentation Foundation,
grafica se va adapta automat la dispozitivul grafic existent,astfel incat
utilizatorii vor vedea aceeasi imagine,indiferent de configuratia hardware
si software instalata.In plus,liniile,poligoanele sau elipsele sunt niste
obiecte visuale,la fel ca restul obiectelor din interfata si pot fi
configurate cu ajutorul ferestrei Properties.
EXEMPLU:
Prima etapa consta in alegerea obiectelor necesare.Sa presupunem ca
doriti sa desenati doua linii,o elipsa si sa adaugati un text explicativ.
Obiectele pentru grafica 2D nu sunt adaugate implicit in Toolbox,asa ca
alegeti din meniul Tools,optiunea Choose Toolbox Items,apoi selectati
meniul WFP Components si selectati componentele Ellipse,Line si TextBox.
In continuare,puteti lucra in fereastra de design.Adaugati obiectele si
apoi setati proprietatile dorite in fereastra Properties.Adaugati prima
linie si setati proprietatile: X1="20" Y1="20" X2="100" Y2="100".Alegeti
o culoare pentru Stroke,de exemplu Chocolate si apoi setati grosimea
liniei cu StrokeThickness="3".Adaugati o noua linie si setati: X1="170"
Y1="100" X2="240" Y2="20" si alegeti aceeasi culoare si grosime a liniei.
Apoi adaugati un obiect de tip Ellipse si setati urmatoarele proprietati:
Width="200" Height="50" Fill="Bisque" Stroke="Red".
Obiectele grafice pot fi si suprapuse.De exemplu,pentru a include o
alta elipsa in cea precedenta,adaugati un nou obiect de tip Ellipse si
apoi setati proprietatile: Width="50" Height="20" Fill="Coral" si
Stroke="Red". Implicit,elipsele vor fi centrate in mijlocul ferestrei.
Daca doriti sa schimbati punctul de referinta,puteti utiliza proprietatile
HorizontalAligment si VerticalAlignment.
Pentru a putea adauga o eticheta explicativa,adaugati si un obiect de
tip TextBox si setati proprietatile: Text="Desen1" Margin="40,200,40,20"
si Background = "Azure".
Compilati si executati cu F5.
Acelasi rezultat se poate obtine si daca preferati sa editati direct
codul in fereastra Window1.xaml.In acest caz,adaugati in tag-ul Grid
semnul < si apoi puteti utiliza instrumentul InteliSense pentru a putea
seta proprietatile dorite.
Exemplu: Dupa < editati un L si alegeti Line,apasati bara de spatiu si
apoi alegeti X1 si completati valoarea dorita,apasati din nou bara de
spatiu si alegeti Y1...etc.In final,tag-ul pentru prima linie va fi:
<Line X1="20" Y1="20" X2="100" Y2="100" Stroke="Chocolate"
StrokeThickness="3"></Line>
Studiati putin documentatia tehnica pentru fiecare dintre obiectele
grafice si apoi tatonati putin proprietatile,pentru a vedea ce fel de
efecte grafice puteti obtine.Cu un minimum de efort,se vor putea afisa
grafice complexe sau desene si schite de proiect...etc.Nu ezitati sa
incercati si nuante pastelate, sau culori de fond (background).

-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

Cea mai frecventa aplicatie pentru grafica 2D o reprezinta diagramele


si graficele de functie.Pentru o astfel de aplicatie,se utilizeaza o scara
de valori,etichete si legende,precum si un set de obiecte grafice prin
care se ilustreaza seturi de valori. Diagrama poate fi sub forma de grafic
bi sau tridimensional, pie chart, grafic de functie, flow chart etc.
De cele mai multe ori, valorile prezentate se schimba dinamic,sau cel
putin periodic.Din acest motiv,cea mai simpla solutie este ca valorile sa
fie arhivate sub forma de pereche de puncte,fie intr-o fila text,fie
intr-un tabel sau o baza de date.Valorile vor fi citite si prelucrate
doar in momentul executiei,iar actualizarea valorilor se va putea face
si atunci cand aplicatia este inchisa. Exemplu: o alta aplicatie preia si
actualizeaza datele de la o adresa de Internet.
Pentru acest tip de solutie,fila de resurse se va introduce in proiect
la adresa locala ...BIN/DEBUG.
Exemplu: editati o fila text cu urmatoarele perechi de valori:
0,30 10,30 15,0 18,60 23,30 35,30 40,0 43,60 48,40 100,30 in care
fiecare pereche de valori este pe un rand separat: 0,30
10,30 ...etc
Deschideti un proiect nou de tip WPF si adaugati in fereastra Window1
un buton si un obiect de tip Polyline.Executati un dublu click pe buton
si editati metoda button1_Click astfel:
string path = "Puncte.txt";
string[] readText = File.ReadAllLines(path);
foreach(string sir1 in readText){
string arie2 = sir1.Split(new Char[]{ ','});
double x = Convert.ToDouble(arie2[0]);
double y = Convert.ToDouble(arie2[1]);
polyline1.Points.Add(new Point(x,y));
Compilati si executati cu tasta F5.
Observati ca puteti sa modificati diagrama cu foarte mare usurinta,
adaugand noi perechi de valori,sau modificand cele existente.
In continuare se poate adauga un set de axe de coordonate, o grila de
evaluare, etichete sau o legenda explicativa...etc.
Pentru diagrame complexe,se pot utiliza seturi intregi de astfel de
file de resurse in care fiecare fila arhiveaza valorile pentru un anumit
obiect grafic.Principalul avantaj il constitue faptul ca valorile pot fi
actualizate independent de aplicatie.
Atunci cand valorile se modifica dinamic,in timpul executiei,se poate
adauga un timer,pentru a citi si actualiza periodic filele de resurse.
Un astfel de exemplu tipic este citirea si interpretarea unui curent
electric asincron,sau a unei electrocardiograme.
Interfata grafica poate adauga si optiuni prin care utilizatorul sa
poata interveni interactiv: poate schimba fila de resurse,poate schimba
aspectul sau caracteristicile liniilor, poate selecta subseturi de valori,
poate aplica diverse formule de calcul aspura valorilor citite sau poate
activa diverse filtre pentru eliminarea valorilor extreme etc.
Diagramele au o valoare deosebita atunci cand se prezinta seturi mari
de valori,sau analize statistice,pentru a prezenta o imagine de ansamblu
mai greu de imaginat in absenta unei reprezentari vizuale.

-139-
Exemple Grafica 3D

Ecranul monitorului PC este plat.Din acest motiv,in majoritatea


situatiilor curente,grafica 2D este satisfacatoare pentru prezentarea
grafica a imaginilor dorite.Este simplu de implementat,exista suficiente
obiecte grafice predefinite,ocupa putina memorie si timpul de procesare
este foarte scurt.Atunci cand este necesar,grafica 2D poate fi inlocuita
foarte usor cu o fotografie,sau cu un clip animat,pentru a putea prezenta
imaginii mult mai detaliate.
Exista insa si situatii un care este necesara si o proiectie 3D unui
obiect ce nu poate fi fotografiat.Exemplu: in proiectare,atunci cand se
creaza o imagine virtuala a unui obiect care nu exista inca.Grafica 3D,
este un proces complex,ce implica un volum foarte mare de date si un
numar foarte mare de operatii ce trebuiesc procesate / unitatea de timp,
pentru a forma o imagine complexa,cu iluzie optica de perspectiva.Si in
cazul graficii 3D,ecranul este tot plat.Iluzia spatiala se creaza prin
niste artificii de proiectare a imaginilor 2D,astfel incat sa creeze
senzatia de perspectiva.Practic,fiecare suprafata a obiectului 3D este
creata dintr-un numar mai mic sau mai mare de triunghiuri.Cu cat numarul
de triunghiuri este mai mare,cu atat rezolutia imaginii va fi mai buna,
asemanator cu modul in care imaginea 2D se formeaza dintr-un numar
oarecare de pixeli.Pentru a crea astfel de suprafete,se creaza o retea
formata dintr-un numar cat mai mare de puncte disticnte.Cu cat reteaua
are mai multe puncte,cu atat se vor putea forma mai multe triunghiuri,ce
vor putea reda forme geometrice foarte variate.Cu alte cuvinte,imaginea
3D este doar o aproximatie,mai mult sau mai putin apropiata de froma
reala.Platforma WFP nu este conceputa special pentru grafica 3D,dar
permite prezentarea realista a unor astfel de imaginii,atunci cand sunt
indispensabile in proiect.
Nu trebuie abuzat de acest mijloc tehnic.O simpla sfera,sau un cub,
necesita un numar destul de mare de obiecte si operatii si consuma destul
de mult din capacitatea de lucru a procesorului.Aceeasi imagine se poate
obtine si cu grafica 2D,daca se adauga lumini si umbre sau degrade-uri
de culoare.Din pacate,grafica 3D a fost exploatata intensiv,mai ales
pentru productia de jocuri PC,multe dintre ele de o calitate indoielnica,
dar cu un efort maximal de procesare.Acest gen de "fortare" la maximum
a mijloacelor de expresie grafica,contribuie la vanzarea de hardware cat
mai performant,dar consuma inutil spatii imense de memorie software si
genereaza un numar imens de operatii procesate inutil.Aceste operatii
inutile nu afecteaza doar calculatorul personal,ci afecteaza intreaga
retea Internet, blocata cu incarcarea si descarcarea unor astfel de
jocuri stupide "on line".Din acest motiv,este recomandabil sa nu apelati
la grafica 3D,decat daca este absolut necesar.
Obiectul cheie pentru grafica 3D este Viewport3D.Acest obiect creaza
un fel de suprafata de tip Canvas,in care se va putea defini reteaua in
care se vor proiecta triunghiurile necesare.Atunci cand sunt necesare si
efecte de animatie,nodurile din aceasta retea vor forma scheletul pe care
se vor deplasa triunghiurile care formeaza imaginea.Pentru a accentua
efectul spatial se utilizeaza proprietatea sa denumita Camera,ce permite
ca reteaua de puncte sa fie construita cu stiluri diferite.Obiectul Camera
permite trei tipuri diferite de prezentare:PerspectiveCamera,Orthographic-

-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.

EXEMPLU: -deschideti un proiect nou WFP


-adaugati in fereastra Window1.xaml obiectul ViewPort3D:
<ViewPort3D Name="mainViewPort" ClipToBounds="True">
<Viewport3D.Camera>
<PerspectiveCamera FarPlaneDistance="100" LookDirection="-11,-10,-9"
UpDirection="0,1,0" Position="11,10,9" />
</ViewPort3D.Camera>
<ModelVisual3D><ModelVisual3D.Content>
<DirectionalLight Color="Yellow" Direction="-2,0,-2" />
</ModelVisual3D.Content></ModelVisual3D>
</Viewport3D>
Apoi adaugati un buton in fereastra si executati un dublu click.In
fereastra Window1.xaml.cs adaugati: using System.Media.Media3D; apoi
editati metoda button1_Click astfel:
MeshGeometry3D triangleMesh = new MeshGeometry3D();
Point3D[] points = new Point3D[1000];
random r = new Random();
double y;
int count = 0;
for (int z= -5; z < 5; z++){
for (int x= -5; x < 5; x++){
for (int i= -5; i < 5; i++){
y = i;
points[count] = new Point3D(x,y,z);
count += 1; }}}
for (int i=0; i<1000; i++)
triangleMeshPositions.Add(points[i]);
Material material = new DiffuseMaterial(
new SolidColorBrush(Colors.Lime));
GeometryModel3D triangleModel = new GeometryModel3D(
triangleMesh,material);
ModelVisual3D model = new ModelVisual3D();
model.Content = triangleModel;
this.mainViewport.Children.Add(model);
Compilati si executati cu tasta F5.In exemplul de mai sus,puteti
modifica oricare dintre valori si apoi urmariti imaginea obtinuta.
Grafica 3D se caracterizeaza prin faptul ca o serie intreaga de obiecte
participa la constructia unei imagini simple.Munca 3D este o munca in
echipa,in care fiecare obiect contribuie cu o parte dintre elemente.
Orice eroare de sincrinizare poate modifica decisiv imaginea grafica.
Nu toate proprietatile fiecarui obiect sunt obligatorii.In exemplul de
mai sus,am omis setarea proprietatii MeshGeometry3D.TriangleIndices.In
acest caz,se deseneaza automat toate combinatiile de triunghiuri posibile,
fara nici un fel de indexare.
Combinatiile posibile dintre obiectele grafice sunt practic infinite.
La inceput este recomandabil sa utilizati exclusiv solutii standardizate.

-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

In grafica 3D,axele de coordonate nu sunt fixate in coltul de sus din


stanga ferestrei,ca in grafica 2D,ci sunt centrate implicit in mijlocul
obiectului ViewPort3D.Daca redimensionati acest obiect,axele de coordonate
se vor deplasa o data cu centrul acestui obiect.Pornind din centru (0,0,0)
axa OX este pozitiva spre dreapta si negativa spre stanga,axa OY este
pozitiva in sus si negativa in jos, iar axa OZ este pozitiva spre exterior
si negativa spre profunzimea imaginii.Acelasi sistem de coordonate se
utilizeaa si pentru pozitia camerei sau a sursei de iluminare.Pentru a
intelege cat mai bine importanta fiecarui obiect,reveniti la primul
exemplu 3D (cel cu un cub) si modificati succesiv valorile pentru Position
LookDirection sau UpDirection din PerspectiveCamera.
Obiectele 3D sunt relativ greu de definit,dar o data create pot fi
transformate cu usurinta,cu ajutorul unor functii ce actioneaza asupra
tuturor punctelor ce formeaza imaginea grafica.Practic,pentru a deplasa
un grafic 3D,este suficient sa se declare noua pozitie de coordonate si
toate punctele vor fi translatate la noua lor pozitie.Pentru a intelege
mai usor aceste operatii,trebuie sa va ganditi ca punctele ce formeaza un
grafic 3D sunt arhivate sub forma de arie tridimensionala,sau sub forma
de matrice.Orice operatie asupra intregului obiect,se va repeta pentru
fiecare element al ariei sau matricei.
Exista trei tipuri de astfel de transformari ce se pot aplica unor
grafice tridimensinale: translatii,redimensionari sau rotatii.Pentru
translatii se utilizeaza obiectul Translate3DTransform ce permite
deplasarea intregului obiect de la o pozitie la alta prin intermediul
proprietatilor OffsetX,OffsetY si respectiv OffsetZ.Cele trei proprietati
se pot utiliza fie pentru a schimba static pozitia obiectului in etapa
de design,fie pentru a deplasa sau pentru a anima obiectul,in etapa de
executie.Cele trei proprietati pot fi resetate si independent una de
alta.Astfel,daca se va modifica doar proprietate OffsetX,obiectul se va
deplasa pe axa orizontala (fiecare punct va fi translatat cu o valoare
egala cu cea specificata).

-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

Grafica WFP permite obtinerea unor efecte de animatie similare cu


cele obtinute in oricare alt limbaj de programare.Animatia nu este decat
o iluzie optica,obtinuta prin schimbarea imaginilor cu o viteza suficient
de mare.Ochiul uman poate percepe maximum 30 de imagini/secunda,dar efecte
de animatie se obtin si la o alternanta de doua imagini pe secunda.
Exista numeroase mijloace tehnice prin care se pot obtine astfel de
efecte optice.Solutia cea mai veche,era prin programarea unor bucle de
repetitie,cu o comanda de intarziere(Delay,sau Sleep) si un factor de
incrementare sau decrementare a coordonatelor grafice.Mai modern,au fost
introduse timer-ele si thread-urile.Fiecare obiect grafic poate evolua
intr-un thread propriu,guvernat de un timer propriu.Toate aceste solutii
se pot implementa si in grafica WFP.Exista insa un set de solutii simple,
specializate,pe care interfata grafica WFP le ofera cu scopul de a reduce
numarul de obiecte si/sau operatii necesare.
Astfel,obiectul denumit DoubleAnimation ofera un timer creat automat,
o bucla de repetitie si un set de proprietati si metode ce pot controla
fluxul de operatii,intr-un anumit fel.Cu un astfel de obiect,se poate
anima un obiect grafic,folosind una dintre proprietatile acestui obiect.
EXEMPLU: -deschideti un proiect now WFP si adaugati in fereastra
Window1.xaml urmatorul cod:
<Rectangle Name="MyRectangle" Width="100" Height="100" Fill="Blue">
<Rectangle.Triggers>
<EventTrigger RouteEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="Height"
From="100" To="10" Duration="0:0:5"
AutoReverse="True" RepeatBehavior="Forever" />
<DoubleAnimation
Storyboard.TargetName="MyRectangle"
Storyboard.TargetProperty="Width"
From"100" To "10" Duration="0:0:5"
AutoReverse="True" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStorybord>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
Compilati si executati cu F5.Observati urmatoarele elemente:
Este definit un patrulater cu inaltimea si latimea de 100.Cele doua
proprietati: Height si Width sunt controlate cu ajutorul a doua obiecte
de tip DoubleAnimation.Conectarea la obiectul grafic se face cu ajutorul
proprietatii Storyboard.TargetName,iar proprietatea este selectata prin
proprietatea Storyboard.TargetProperty.Cele doua obiecte DoubleAnimation
sunt continute intr-un container comun,denumit StoryBoard si sunt puse in
actiune de obiectul BeginStoryboard ca urmare a evenimentului conectat
la obiectul EventTrigger (Rectangle.Loaded).

-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.

Exemplele prezentate sunt prelucrate dupa tutorialul MSDN de la adresa


http://msdn.microsoft.com.Aceste exemple nu epuizeaza solutiile pentru
animatie oferite de platforma Visual C#,ci doar subliniaza avantajele
oferite de obiectele WFP.Interfata grafica WFP nu este destinata pentru
jocuri grafice sau pentru efecte de animatie complexa.Chiar daca aceste
efecte pot fi realizate tehnic,atunci cand doriti sa creati un joc PC,
este preferabil sa utilizati o platforma special conceputa pentru acest
scop.
Animatia WPF se poate utiliza cu succes si pentru a prezenta dinamic
evolutia unor valori grafice,sau pentru a crea modele de realitate
virtuala (proiecte tridimensionale,interactiuni etc.).
Pentru a amplifica iluzia optica,se pot utiliza si o serie intreaga
de efecte speciale. Exemplu: pentru a sugera elasticitatea unei mingi,
aceasta se va turti in momentul impactului cu un corp rigid pentru a
continua apoi deplasarea in sens contrar,cu revenire la forma initiala.
Pentru acest gen de efecte speciale,exista obiecte sau algoritmi gata
prefabricati ce pot fi importati din reteaua Internet.Inainte de a face
eforturi suplimentare de programare,este bine sa verificati si oferta
din retea.
Obiectele pentru animatia WPF pot fi completate si cu diverse programe
utilitare si instrumente grafice,create de diversi autori,dar exploatarea
acestor programe se va face doar pe raspunderea d-voastra.
Atunci cand interfata grafica prezinta seturi mari de valori,acestea
vor fi arhivate sub forma de file de resurse tip text,sau tip tabel si
baza de date.Conexiunea cu fila de resurse se va face fie stabil (fila
de resurse este importata in proiect), fie dinamic prin deschiderea unui
link spre o fila de resurse din retea.
O solutie mai laborioasa este atunci cand mixati imagini digitale,
pentru a crea un videoclip animat,sau cand mixati diverse tipuri de
resurse video-optice si resurse audio,pentru a obtine efecte speciale
complexe.

-149-
Exemplu Grid

Principala functionalitate pentru Windows Presentation Foundation o


reprezinta designul de interfete grafice cu utilizatorul.In exemplele
precedente,au fost prezentate cateva dintre facilitatile grafice 2D si
3D ce pot fi incluse in intefata.Controalele visuale sunt la fel ca si
in Windows Forms: butoane,imagini,casete de dialog...etc.
Ca o noutate,in loc de Panel sau Canvas,WPF utilizeaza un panou sub
forma de grila pentru organizarea componentelor visuale din interfata.
Proiectul este creat implicit cu un astfel de obiect,denumit Grid ce
functioneaza pe post de container pentru restul controalelor.Implicit,
are o singura coloana si un singur rand si este asemanator cu un obiect
de tip Panel.Insa atunci cand doriti sa organizati componentele din
interfata pe linii si coloane,puteti transforma obiectul intr-o grila
adevarata.
EXEMPLU: -deschideti un proiect nou de tip WFP
-salvati proiectul la adresa dorita
-selectati obiectul Grid si deschideti fereastra Properties
-pentru a observa mai bine panoul,alegeti Background si
setati o culoare oarecare.
-daca doriti,panoul poate sa contina si o imagine.Pentru
acest scop,selectati din meniul Project optiunea Add New Item si alegeti
Resources File,apoi introduceti numele resursei: Sunset.jpg.Se va crea
o referinta si o fila virtuala,nefunctionala.Pentru a adauga imaginea
reala,copiati imaginea cu Copy-Paste.Exemplu: copiati fila Sunset.jpg
din directorul: Documents and Settings\All Users\Documents\My Pictures\
Sample Pictures (in Windows XP).Apoi,adaugati in Window1.xaml codul:
<Grid.Background>
<ImageBrush ImageSource="Sunset.jpg" Opacity="0.9"/>
</Grid.Background>
Pentru a imparti grila in randuri si coloane selectati proprietatile
ColumnDefinitions si respectiv RowDefinitons.Apasati butonul cu trei
puncte,apoi apasati butonul Add pentru fiecare coloana,respectiv pentru
fiecare rand din grila.Fiecare rand si coloana va avea un set propriu de
proprietati ce permit configurarea obiectului.Astfel,daca doriti ca una
dintre coloane sa fie de trei ori mai larga decar restul coloanelor
setati proprietatea Width la valoarea 3*.Daca doriti puteti sa specificati
exact inaltimea randurilor sau largimea coloanelor.Pentru a plasa un
component la o anumita pozitie din grila,se vor utiliza proprietatile
Grid.Row si Grid.Column.
EXEMPLU: -creati trei coloane si trei randuri,apoi adaugati doua
casete de tip text pe primul rand:
<TextBlock FontSize="14" FontWeight="Bold" Grid.Row="0" Grid.Column="0"
Foreground="RED" Text="Autor" Height="20" VerticalAlignment="Top"/>
<TextBlock FontSize="14" FontWeight="Bold" Grid.Row="0" Grid.Column="1"
Foreground="BLUE" Text="Mircea" Height="20" VerticalAlignment="Top"/>
Daca nu se specifica altfel,inaltimea randurilor va fi implict Auto,
adica spatiul ferestrei va fi impartit egal intre randuri.
Pentru un aspect personalizat,experimentati putin modificand una sau
alta dintre proprietatile fiecarui obiect.Incercati sa includeti butoane
si casete de dialog in grila,la pozitia dorita.

-150-
Exemplu Data Binding

Intr-o interfata grafica exista numeroase situatii in care doua sau


mai multe obiecte se interconecteaza pentru a crea o functionalitate
noua.In mod normal,se alege un eveniment oarecare,se editeaza o functie
pentru tratarea evenimentului si respectiv o functie pentru asteptarea
si identificarea evenimentului.Legatura dintre aceste structuri se face
fie prin mesaje Windows,fie prin mesaje specializate.Interfata WPF a
simplificat foarte mult aceste operatii,introducand un mecanism automat,
denumit Data Binding (conectarea datelor),prin care doua sau mai multe
obiecte pot fi conectate cu ajutorul unui obiect special denumit Binding.
Exemplu,pentru ca utilizatorul sa poata personaliza interfata grafica,se
poate adauga un obiect de tip ListBox in care clientul selecteaza culoarea
preferata pentru editarea unui text oarecare.
EXEMPLU: -deschideti un proiect nou WPF
-adaugati in Window1.xaml urmatorul cod:
<StackPanel>
<TextBlock Width="250" Height="25" Text="Alegeti o culoare:"
TextWrapping="Wrap"/>
<ListBox x:Name="setCulori" Width="250" Height="100">
<ListBoxItem Content = "Fuchsia"/>
<ListBoxItem Content = "Aqua"/>
<ListBoxItem Content = "Yellow"/>
<ListBoxItem Content = "Red"/>
<ListBoxItem Content = "Lime"/>
<listBoxItem Content = "Orange"/>
</ListBox>
<TextBlock Width="250" Height="30" Text="Culoarea aleasa este:"/>
<TextBlock Height="30" Margin="15,30,20,0" VerticalAlignment="Top">
<TextBlock.Text>
<Binding ElementName="setCulori" Path="SelectedItem.Content"/>
</TextBlock.Text>
<TextBlock.Background>
<Binding ElementName="setCulori" Path="SelectedItem.Content"/>
</TextBlock.Background>
</TextBlock>
</StackPanel>
Compilati si executati cu tasta F5.
Se observa ca legatura dintre cele doua obiecte se face prin obiectul
Binding,in care se desemneaza obiectul sursa prin ElementName si apoi
se specifica proprietatea obiectului din care se vor prelua datele prin
Path.In exemplul de mai sus,am conectat doua proprietati ale obiectului
de destinatie TextBlock,la aceeasi proprietate din obiectul sursa ListBox,
pentru a afisa atat numele culorii cat si aspectul sau grafic.In mod
similar,se pot face numeroase conexiuni intre proprietatile obiectelor
din interfata,astfel incat un singur eveniment din timpul executiei sa
poata declansa doua sau mai multe transformari,sau evenimente.
Conectarea se poate face si biunivoc.
EXEMPLU: inlocuiti obiectul TextBlock cu TextBox si faceti conexiunea cu:
<Binding ElementName="setCulori" Path=SelectedItem.Content" Mode="TwoWay"/>
Acum puteti si sa editati in TextBox,optiunile din ListBox.Incercati.

-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

Exista numeroase formate standardizate pentru prezentarea complexa a


datelor de tip text,impreuna cu imagini sau alte resurse grafice.Un
astfel de format standard,creat recent de firma Microsoft este si formatul
denumit OpenXPS (pe scurt XPS),precizat prin standardul international
ECMA-388 si validat ca open standard in anul 2009.Acest tip de fila
ofera cam aceleasi avantaje ca si formatul PDF,oferit de firma Adobe,
adica grupeaza si securizeaza toate resursele intr-un container,astfel
incat datele sa fie usor portabile fara riscul de a fi corupte intentionat
sau accidental.In plus,formatul XPS permite gradiente de culori si de
transparente,scheme fixe pentru imprimare,efecte grafice 2D si 3D,zoom,
vocabular,cautarea unui cuvant...etc.In plus,in acest format se poate
imprima aspectul real al unei interfete grafice,cu butoane si casete de
dialog,asa cum apare in timp real,in momentul executiei.
Formatul XPS este inclus nativ in Windows Vista si in Windows7 si va
fi inclus in toate versiunile viitoare ale sistemului de operare Windows,
asa ca este probabil sa fie unanum acceptat pentru schimbul de date,sau
de informatii.In cazul sistemului de operare Windows XP,driverele pentru
formatul OpenXPS se instaleaza automat,impreuna cu cele pentru WPF.
Pentru a edita un astfel de document,deschideti orice fila de tip .doc
sau .html si alegeti optiunea Print,apoi selectati Microsoft XPS Document
Writer si completati wizard-ul.Fila nou creata va grupa toate resursele
si asigura un nivel ridicat de securitate.
Interfata WPF ofera un set intreg de obiecte create special pentru a
facilita crearea sau prezentarea de astfel de documente.In principiu,
documentele create pot fi de doua tipuri: statice (fixed) si dinamice
(flow documents).Cele statice nu permit modificarea aspectului sau a
continutului.Un astfel de document va fi afisat la fel,indiferent de
contextul de dispozitiv grafic al utilizatorului si va fi imprimat la fel,
indiferent de tipul si performantele imprimantei utilizate.Acest gen de
documente se utilizeaza mai ales pentru actele oficiale,pentru imprimate
si chestionare,sau pentru orice alt document ce trebuie sa fie identic
pentru toti utilizatorii.Documentele dinamice sunt prin excelenta
adaptabile la orice tip de dispozitiv grafic si la orice tip de schimbari
si transformari interactive.Un astfel de document va permite orice tip
de operatii grafice posibile,schimbarea continutului prin interactiunea
cu utilizatorul,actualizarea permanenta a datelor...etc.
Documentele statice se construiesc din obiecte de tip TextBlock intr-un
obiect de tip FixedDocument.Se pot utiliza si alte componente visuale,doar
atunci cand includerea lor in document este utila.De exemplu,pentru a
prezenta modul de completare a unui formular,se pot introduce si casete
de tip TextBox,sau butoane cu selectie multipla de tip CheckBox,dar
aceste butoane nu vor fi functionale in timpul executiei.Pentru a putea
transforma documentul FixedDocument,intr-un document de tip XPS,se poate
utiliza un obiect de tip DocumentViewer.Trebuie mentionat faptul ca nu
se poate construi documentul static direct in DocumentViewer,ci trebuie
sa fie construit separat si apoi sa fie importat in obiectul de tip
DocumentViewer.Dupa import,se apasa butonul pentru imprimare si se alege
optiunea XPS Document Writer pentru a crea o fila XPS,sau se alege o
imprimanta pentru a imprima datele.

-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

Toate exemplele de pana acum,folosesc ca suport grafic al aplicatiei


o fereastra clasica de tip Windows.Dezvoltarea impetuoasa a retelei
Internet,a impus insa un standard nou pentru prezentarea datelor.Sute de
milioane de site-uri si blog-uri prezinta datele sub forma de file HTML,
XML,XAML,sau combinatii ale acestora.Pentru a permite dezvoltarea de
resurse ce pot fi afisate direct in site,sau in server,platforma Microsoft
Visual C# Express Edition include si un utilitar specializat.
Aplicatiile de tip XAML Browser sunt aproape identice cu cele de tip
WPF,cu diferenta ca se utilizeaza un container de tip Page in loc de
Window,iar aplicatia va fi deschisa direct in Browserul implict pentru
Internet.Acest gen de aplicatie,combina toate avantajele platformei WPF,
cu cele ale formatului HTML si XAML.In aceeasi interfata grafica se pot
utiliza controale vizuale,combinate cu grafica 2D si 3D si conectate
intre ele prin Data Binding.Proiectul final se asambleaza intr-o fila
cu extensia .XBAP ce poate fi executata direct cu un click,sau poate fi
introdusa intr-o pagina HTML,printr-un link <a href="fila.xbap">.Daca
se exporta proiectul,este necesar ca fila .xbap sa fie insotita si de
fila cu extensia .exe,respectiv de fila cu extensia .manifest.
Aplicatiile de tip XAML Browser sunt extrem de usor de realizat,mai
ales atunci cand urmeaza sa fie executate in server-ul administrat de
creatorul aplicatiei.Insa,atunci cand aplicatia utilizeaza si resurse
externe,accesul la aceste resurese va fi guvernat de protocoalele de
securitate locale ale browser-ului instalat.Este foarte probabil ca
fiecare server va avea protocale de securitate putin diferite,astfel ca
in caz ca doriti sa distribuiti aplicatii tip Web Browser ce includ si
link-uri sau resurse externe,trebuie sa insotiti aplicatia de explicatii
detaliate privind setarea protocoalelor se securitate.In plus,pentru a
putea utiliza resursele locale,fiecare client va trebui sa reseteze
optiunile browserului astfel incat sa permita citirea sau scrierea unor
file locale ( Allow active content to run on files in My Computer).
Acest gen de setare,deschide o poarta de acces spre calculatorul user si
expune sistemul la atacul "virusilor" din retea.Majoritatea clientilor nu
vor accepta acest risc.Din acest motiv,este recomandabil sa utilizati
acest tip de aplicatie doar pentru filele ce urmeaza sa fie incarcate in
site-ul sau in blog-ul d-voastra.Pentru aplicatiile ce urmeaza sa fie
distribuite la clienti,este de preferat formatul Windows.

Exemplu pentru XAML Browser application

Aplicatiile de tip Browser se proiecteaza si programeaza la fel ca si


cele de tip WPF.Dupa ce precizati scopul propus,alegeti componentele
visuale si formulele de calcul dorite,apoi treceti la etapa de design.
In multe situatii,componentele visuale pot fi interconectate intre ele,sau
se pot utiliza obiecte ajutatoare,pentru a forma functionalitati noi.De
exemplu,in locul unui bucle de control al unei variabile se poate utiliza
cu succes un control de tip Slider,conectat la obiect prin Data Binding.
Sa presupunem ca doriti sa prezentati o imagine grafica ce poate fi
transformata si actualizata cu ajutorul unor obiecte visuale din interfata
grafica (un grafic dinamic):

-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

Toate exemplele din acest manual au fost afisate in consola,sau intr-o


interfata grafica unica de tip fereastra Window sau Page.Totusi,atunci
cand programul are o functionalitate complexa si necesita un numar mare
de controale grafice,este recomandabil ca interafta grafica cu clientul
sa fie partajata de mai multe ferestre.Un astfel de program conceput
modular,nu numai ca este mai robust si mai usor de executat,dar este si
mult mai usor de transformat sau actualizat.Fiecare fereastra este un
modul distinct ce poate fi inlocuit sau transferat dintr-o aplicatie in
alta.Indiferent daca este o fereastra Windows Forms sau WPF,fereastra va
functiona pe post de container,pentru toate componentele visuale si
codurile anexate acestora.
EXEMPLU: -deschideti un proiect nou de tip WPF
-adaugati un buton in fereastra
-din meniul Project,alegeti optiunea Add Window si adaugati
in proiect si fereastra Window2.xaml
-adaugati componente si coduri functionale, apoi utilizati
fereastra Solution Explorer pentru gestionarea resurselor
-pentru a conecta cele doua ferestre intre ele,reveniti la
fereastra Window1.xaml,executati un dublu click pe proiect si completati
metoda button1_Click astfel:
Window1 fereastra1 = new WpfApplication1.Window2();
fereastra1.Visibility = Visibility.Visible;
Compilati si executati cu tasta F5.Observati ca trebuie creat un obiect
nou.Fereastra Window2 adaugata in proiect nu este decat o clasa noua,la
fel ca oricare alta clasa din proiect.Designul grafic si functionalitatea
se poate proiecta si codifica la fel ca pentru fereastra principala,dar
pentru a putea functiona in program,trebuie creat explicit un obiect din
clasa respectiva.WpfApplication1 este numele implict al proiectului.Daca
proiectul este denumit altfel,trebuie sa utilizati calea reala de acces
la resursa: NumeleProiectului.Window2();
In mod similar,se pot adauga toate resursele necesare in program.
Din meniul Project,alegeti Add New Item si apoi tipul de resursa dorit:
About Box,Class,Code File, Custom Control, DataSet, Local Database, Text
File, User Control...etc.
Nu se pot formula recomandari generale.Pentru fiecare tip de resursa,
este bine sa studiati si documentatia,inainte de a implemnta resursa in
proiect.Daca nu aveti experienta,puteti sa dezasamblati un proiect
asemanator si sa folositi modularitatea,schimband codurile.Nu se pot
forma sabloane standard si nici nu este recomandabil,deoarece in acest
mod s-ar orienta stilul de programare spre un anumit tip de solutie,
limitand astfel posibilitatile de expresie.Fiecare programator va alege
tipul de solutie,in functie de necesitati,dar si in functie de experienta
sa anterioara,sau in functie de limitele sale.In principiu,cu cat un
program este format din blocuri mai mici de coduri executabile,cu atat
va fi mai maleabil,mai usor de incarcat in memorie,mai rapid la executie
si mai putin probabil sa se blocheze prin lipsa de memorie RAM.Obiectele,
containerele si modulele nu fac decat sa fragmenteze memoria in blocuri
cat mai usor de manevrat si gestionat.Conectarea acestor module,este ca
in joc de puzzle,cu solutii multiple.

-160-
Concluzii generale,recomandari

Limbajul C# este un limbaj de programare complet,structurat sub forma


de obiecte,ce poate satisface orice necesitati de programare.Nucleul
limbajului exploateaza platforma .Net,unde sunt arhivate majoritatea
claselor si intefetelor standard.La acestea se pot adauga controalele de
tip Active X,sau resursele create de alti programatori.
Dat fiind numarul mare de resurse accesibile,solutiile de programare
acopera un domeniu practic infinit.In plus,platforma .Net si controalele
Active X sunt in permanenta dezvoltare.Nu se pot formula recomandari
general valabile.Acest manual a schitat doar,principalele mijloace de
expresie si un numar oarecare de solutii exemplificative.Subiectul este
insa departe de a fi epuizat.Pentru completarea studiilor se pot utiliza
toate tutorialele si cartile e-book din retea,precum si alte materiale
informative de tip: Webcast,video,virtual lab,podcast...etc.
Platforma Microsoft Visual C# Express Edition este un instrument ideal
pentru realizarea aplicatiilor.Exista numeroase tutoriale si materiale
video specializate in prezentarea acestei platforme.Totusi,nu este bine
sa incepeti studiul limbajului direct in platforma visuala.Numarul mare
de resurse poate genera numeroase situatii confuzive,chiar si atunci cand
se apeleaza la utilitarul InteliSense.Este bine sa incepeti cu exemple
cat mai simple si mai clare,compilate cu csc.exe.Dupa ce va familiarizati
cu tipurile de date si modul de formare a expresiilor,este bine sa
exersati cat mai multe clase si expresii cu mebrii acestora.In final,
puteti incerca ca editati manual cateva interfete grafice functionale.
Dupa ce reusiti sa editati programe functionale cu csc.exe,platforma
visuala va fi o adevarata binecuvantare.Nu numai ca va simplifica foarte
mult toate operatiile de design,dar adauga si un numar mare de utilitare.
Toate resursele sunt la indemana,la un click de mouse.
Exemplele din acest manual sunt complet nespecifice.Pentru a va
simplifica munca viitoare,este bine sa va construiti mici module si clase
specializate,ce pot fi apoi incluse in orice alta aplicatie.Un astfel de
modul,va executa un anumit gen de calcule,va prelucra un anumit gen de
resurse,sau va prezenta grafic un anumit gen de date...etc.Modulele vor
fi create in functie de specificul domeniului in care activati.
Exemple: -un modul care extrage si prelucreaza date economice de la
adrese fixe din retea
-un modul care calculeaza costurile unui transport de marfuri
pe un anumit traseu
-un modul care extrage stirile sportive din ziarele preferate
-un modul care inregistreaza zilnic operatiile executate intr-o
aplicatie oarecare
-etc.
Programarea si exploatarea resurselor,este o activitate colectiva,de
interes general.Daca modulele create de d-voastra sunt functionale si
portabile,puteti contribui la efortul general oferind aceste module in
reteaua Internet,dar nu inainte de a studia conditiile standard necesare.

***** S F A R S I T ******

S-ar putea să vă placă și