Limbajul c sharp



Yüklə 1,48 Mb.
səhifə8/14
tarix08.01.2019
ölçüsü1,48 Mb.
#92362
1   ...   4   5   6   7   8   9   10   11   ...   14

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

protected static List students = new List {

new Student { Nume = "Albu Mihai",

Punctaj = new List{ 79,82,81,79 }},

new Student { Nume = "Pop Maria",

Punctaj = new List{ 99,76,90,94 }},

new Student { Nume = "Pop Victor",

Punctaj = new List{ 93,72,80,87 }},

new Student { Nume = "Georgescu Lucia",

Punctaj = new List{ 97,79,85,82 }},

new Student { Nume = "Ionescu Ion",

Punctaj = new List{ 85,92,91,90 }},

new Student { Nume = "Barbu Constantin",

Punctaj = new List{ 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 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.

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


, , ,
, , , ,

,

, ,


, , ,

, , si .

Fiecare tag se utilizeaza intr-un anumit context.De exemplu,se poate

utiliza text pentru a delimita in cadrul textului o linie de cod

oarecare,sau se poate utiliza text.... 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 ....

EXEMPLU:


using System;

// compilare cu csc Xmldoc1.cs /doc:Documentatie.xml

/// text pentru clasa TestClass

public class TestClass

{

///

DoWork este o metoda din clasa TestClass


Yüklə 1,48 Mb.

Dostları ilə paylaş:
1   ...   4   5   6   7   8   9   10   11   ...   14




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©muhaz.org 2024
rəhbərliyinə müraciət

gir | qeydiyyatdan keç
    Ana səhifə


yükləyin