Pag. 1 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
IOAN ASIMINOAEI
Email: [email protected]
Mod de adresare pentru e-mail :
In subject veti scrie cine sunteti (Nume, prenume
complet, anul si forma de studii, Tema abordarii)
Evaluare
(60 * puncte_laborator + 40 * puncte_test_scris) / 1000
Puncte_Laborator >= 60, maxim = 100
Puncte_test_scris >= 40, maxim = 100
Laboratoarele nu se pot recupera !
Fiecare student va respecta orarul
grupei din care face parte !
Nu se da test partial. Evaluarea de la
mijlocul semestrului este cea de la
laborator.
Pag. 2 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Bibliografie
1. Tom Archer : Inside C# Second Edition
2. Tom Barnaby : Distributed .NET Programming in C#
3. Joseph C. Rattz, Jr. : Language Integrated Query in C# 2008
4. Chris Sells, Michael Weinhardt: Windows Forms 2.0
Programming
5. Andrew Troelsen: Pro C# 2008 and the .NET 3.5 Platform,
Fourth Edition
6. MSDN
7. Steve Resnik, Richard Crane, Chris Bowen: Essential Windows Communication Foundation for .NET Framework
3.5
8. Charles Petzold: Programming Windows with Windows
Forms ...
9. David Sceppa : Programming ADO.NET
10. codeproject, codeguru, etc.
Utilitare
CSharpDeveloper – free
DevCSharp Express – free (limitat la anumite tipuri de aplicatii).
Visual Studio 2008, 2010, 2013, 2015
Reflector, ILSpy - decompilatoare
FxCop
ILDASM – Microsoft (dezasamblor)
Pag. 3 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Curs 1-2: Arhitectura .NET Framework
Cuprins
Arhitectura .NET Framework
� CLR – Common Language Runtime
o CTS – Common Type System
o CLS – Common Language Specification
� BCL (FCL) – Base Class Library (Framework Class
Library)
o Tipuri de aplicatii ce pot fi dezvoltate sub aceasta platforma
o Spatii de nume
� Trasaturi principale ale limbajului C#.
� Tipuri
o Tip Valoare o Tip Referinta
� Metode
o ale instantei o statice
� Modificatori de acces pentru tip.
� Constructori. Constructori statici.
� Clase statice. Metode extinse.
� Proprietati. Delegates. Evenimente.
� Mostenire.
� Polimorfism.
Pag. 4 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Arhitectura framework .NET poate fi reprezentata astfel :
• Biblioteca de clase de baza din .NET (BCL)
• Common Language Runtime (CLR)
Componente principale pentru .NET:
• CLR – Common Language Runtime;
o CTS – Common Type System;
o CLS – Common Language Specification;
• BCL / FCL – Base Class Library / Framework Class Library.
Trasaturi .NET:
• Interoperabilitate cu codul existent (COM poate interopera cu .NET si invers, apel
functii din C/C++);
• Integrare completa si totala a limbajului (mostenire intre tipuri create in limbaje
diferite, manipularea exceptiilor, depanare) ;
• Motor de runtime comun tuturor limbajelor (CLR) ;
• Biblioteca unica de clase (FCL/BCL) ;
• Constructia componentelor COM mult mai usoara (nu e nevoie de IClassFactory,
IUnknown, IDispatch, cod IDL, etc.) ;
• Model simplificat de distribuire a aplicatiilor (nu mai e nevoie de inregistrare in
registri, se permit multiple versiuni ale aceleasi biblioteci *.dll) .
• etc.
Pag. 5 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
C# Limbaj dezvoltat de MS pentru .NET.
Toate exemplele din curs vor fi date folosind acest limbaj.
Trasaturi principale ale acestui limbaj :
• Nu se mai lucreaza cu pointeri ;
• Management automat al memoriei (C# nu suporta « delete » pe un obiect creat) ;
• Supraincarcarea operatorilor ;
• Suport pentru programarea bazata pe atribute ;
• Tipuri generice ;
• Suport pentru metode anonime ;
• Simplificari in implementarea modelului « delegate/event » ;
• Abilitatea de a defini un singur tip in mai multe fisiere – « partial keyword» ;
• Suport pentru cereri – LINQ;
• Suport pentru tipuri anonime;
• Abilitatea de a extinde functionalitatea unui tip existent via metode extinse;
• Operatorul lambda (=>) ce simplifica lucrul cu delegates;
• Sintaxa de initializare a unui obiect nou creat ce da posibilitatea de a seta valorile
proprietatilor in momentul crearii obiectului.
Pag. 6 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Definitii
Cod managed = cod gazduit de platforma .NET (scris in limbaje acceptate de .NET, ex. C#,
VB .NET, F#, etc.);
Fiecare tip este descris de metadata. Metadata este construita de compilator.
Cod unmanaged = cod ce nu e specific platformei .NET (de regula scris in alte limbaje, ex.
C, C++, Pascal, etc.).
Assembly = unitatea binara ce contine cod managed (definitie prescurtata) – poate avea
extensia dll sau exe dar ca structura sunt diferite fata de fisierele dll/exe din COM sau aplicatii
Win32.
Un assembly poate fi gazduit intr-un singur fisier (single file assembly) sau poate fi constituit
din mai multe fisiere (multifile assemblies). In cazul multifile assemblies unitatile
componente se numesc module. Cand se construieste assembly (multifile) unul din aceste
module este modulul primar ce va contine manifest (metadata).
Concluzie
Un assembly este o grupare logica de unul sau mai multe module ce este distribuit si
« versionat » ca o singura unitate.
Toate compilatoarele ce lucreaza sub platforma .NET emit cod (MS)IL si metadata.
Cod sursa (C#) -> Compilator C# -> fisier ce contine cod IL si metadata.
IL (Intermediate Language) referit ca CIL (Common Intermediate Language – ultima
denumire acceptata) sau MSIL.
Codul CIL este compilat in instructiuni specifice CPU de pe masina.
Entitatea ce compileaza codul se numeste just-in-time (JIT) compiler sau Jitter.
Pag. 7 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Metadata
Pe langa instructiunile CIL, un assembly .NET contine metadata care descrie fiecare tip
(class, struct, enum, etc.) precum si membrii fiecarui tip (proprietati, metode,
evenimente, date membru, etc.).
Metadata poate fi inetrogata folosing reflection.
Manifest – metadata pentru assembly
Contine informatii despre assemblies necesari si assembly-ul curent pentru a functiona
corect : versiune, informatii despre copywright, etc.
Pag. 8 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
CTS – Common Type System
CTS – este o specificatie – descrie cum sunt definite tipurile si modul de comportare al
acestora.
Un tip poate contine zero sau mai multi membri.
Ce contine CTS?
class ; struct ; enum ; delegate ; event ;
membri (tipuri preconstruite sau tipuri din BCL sau tipuri construite de dezvoltatori) ;
Spatiul de nume (namespace)
Namespace constituie o grupare logica de tipuri, continute intr-un assembly.
Exemplu
System.IO contine tipuri ce permit lucrul cu fisiere ;
System.Collections.Generic contine tipuri pentru colectii generice ;
System.Collections contine tipuri pentru colectii non-generice, etc.
Daca o aceeasi aplicatie este dezvoltata in VB .NET si apoi in C#, acestea vor folosi acelasi
spatiu de nume.
Pentru a accesa un anumit spatiu de nume se foloseste directiva using si se adauga o
referinta in proiect la assembly-ul respectiv.
Observatie :
Pentru System nu trebuie adaugata referinta la assembly.
Majoritatea assemblies-urilor din .NET framework sunt localizati in GAC (Global Assembly
Cache).
Exemplu
// Hello world in C# using System; public class Hello {
static void Main() {
Console.WriteLine("Hello world - C#"); }
} ' Hello world in VB Imports System Public Module Hello Sub Main()
Pag. 9 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Console.WriteLine("Hello world - VB") End Sub End Module // Hello world in C++/CLI #include "stdafx.h" using namespace System; int main(array<System::String ^> ^args) {
Console::WriteLine(L" Hello world - C++/CLI"); return 0;
}
Observatie:
1. Se foloseste acelasi spatiu de nume System. Este in topul ierarhiei.
2. Se foloseste aceeasi metoda statica WriteLine din clasa Console.
3. Sintaxa difera de la limbaj la limbaj.
Pag. 10 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
BCL – Base Class Library (FCL – Framework Class Library)
CLR si BCL permit dezvoltarea urmatoarelor tipuri de aplicatii:
XML Web services;
Serviciile Web sunt construite la un nivel superior protocoalelor HTTP, XML, SOAP,
protocoale ce permit componentelor sa comunice independent de sistemul pe care se afla.
Spatiul de nume System.Web.Services defineste tipurile ce asigura functionalitatea
serviciilor Web
Web Forms – aplicatii bazate pe HTML
ASP.NET furnizeaza un set complet de tipuri pentru dezvoltarea de aplicatii bazate pe Web.
Acesta ofera compilarea dinamica a paginilor Web, abilitatea de a scrie scripturi in mai multe
limbaje .NET si abilitatea de a reutiliza tipuri .NET din pagini Web. Clasele principale pentru
ASP.NET se gasesc in spatiul de nume System.Web.UI.Page.
Windows Forms
Spatiul de nume System.Windows.Forms al cadrului de lucru .NET furnizeaza tipuri ce
suporta crearea de aplicatii cu interfata grafica (GUI), aplicatii dezvoltate pentru sistemele de
operare Windows. Tipurile din aceast spatiu de nume sunt similare in functionalitate cu
clasele din MFC sau alte biblioteci de clase (wxWidget). Important este ca tipurile din .NET
pot fi utilizate de orice limbaj compatibil cu .NET.
Clasele ajutatoare pentru dezvoltarea aplicatiilor Windows se gasesc sub acest spatiu de
nume : clase pentru butoane, controale, dialoguri, combo boxuri, etc.
Windows Console Applications – aplicatii de tip consola (CUI – Console User Interface).
WPF – Windows Presentation Foundation – aplicatii cu interfata grafica – GUI.
Windows services – aplicatii controlate de Windows Service Control Manager folosind
.NET.
Component library – componente dezvoltate de utilizator ce pot fi folosite in alte aplicatii
sau componente.
Exemple de spatii de nume
Spatiu de nume Descriere System Contine tipurile de baza folosite de orice aplicatie; System.Collections Contine tipurile ce gestioneaza colectii de obiecte ; include
colectiile stive, cozi, tabele hash, etc. System.Drawing Tipuri pentru grafice. System.Globalization Tipuri pentru NLS - National Language Support –comparare
stringuri, formatare, calendar. System.IO Tratare fisiere. System.Net Tipuri ce permit comunicatia in retea. System.Management Folosit in WMI (Windows Management Instrumentation). System.Reflection Permite inspectia metadatei.
Pag. 11 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Tip valoare. Tip referinta.
Aceste tipuri reflecta modul cum variabilele sunt alocate si cum functioneaza acestea intern.
Toate tipurile se creaza folosind operatorul new.
Observatie.
Folosirea operatorului new nu inseamna ca tipurile se aloca in heap.
Declarare variabile si initializare
Ierarhia de clase a tipului sistem
Tot ce este derivat din System.ValueType se aloca pe stiva (tip valoare), orice altceva se
aloca in heap (tip referinta) si este eliberat de catre garbage collector.
Echivalente int <=> System.Int32 int n <=> System.Int32 n ;
Pag. 12 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Tipurile numerice din .NET suporta proprietatile MinValue si MaxValue.
Tip valoare : variabila contine valoarea.
� Nu poate fi null (o variabila de tip value are intotdeauna o valoare) daca nu e declarat
“nullable”.
� Folosirea unei asemenea variabile ca argument al unei functii are ca efect pasarea
valorii. Modificarea valorii in cadrul functiei este locala.
Tipurile “nullable” sunt instante ale structurii System.Nullable<T>. Unui tip « nullable »
i se poate atribui o valoare conform tipului T sau valoarea null. Abilitatea de a atribui null
la tipuri numerice sau boolean este folositoare cand se lucreaza cu baze de date sau pentru a
marca faptul ca o variabila nu a fost initializata.
De exemplu un tip numeric intr-o tabela dintr-o baza de date poate avea o valoare sau poate fi
nedefinit (null).
Exemplu:
// se aloca 32 biti pentru nVarsta iar valoarea este 22 System.Int32 nVarsta = 22; // <=> int nVarsta = 32 ; // m este de tip int si nullable int ? m = null; // <=> Nullable<System.Int32> m = null ;
Pentru a determina valoarea unui asemenea tip se folosesc proprietatile HasValue si Value
ca in exemplul de mai jos :
class ExNullable { public static void Main() { int? m = null; if (m.HasValue == true) { System.Console.WriteLine("m = " + m.Value); } else { System.Console.WriteLine("m = Null"); } } }
De fiecare data cand declaram o variabila de un anumit tip, sistemul aloca numarul de octeti
asociati tipului respectiv si se poate lucra in mod direct cu memoria alocata.
Intrebare :
Un tip valoare poate contine tip referinta ?
Raspuns : DA.
Reamintim ca tipul struct este tip valoare.
Putem construi o structura ce contine ca data membru un tip referinta.
Sa analizam urmatorul cod.
Pag. 13 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
// definim un tip referinta public class Info { string HostName; public Info() { HostName = Environment.MachineName; } public void PrintHostName() { Console.WriteLine("Machine Name = {0}", HostName); } }
/// <summary> /// Definim un tip valoare ce contine un tip referinta /// </summary> public struct InfoStructure { // tip referinta public Info info; // tip referinta public string SystemDirectory; public int an; // tip valoare public InfoStructure(Info _info, string _sd) { info = _info; SystemDirectory = _sd; an = 2012; } public void PrintInfoStructure() { Console.WriteLine("PrintInfoStructure"); info.PrintHostName(); Console.WriteLine("System directory : {0}", SystemDirectory); Console.WriteLine("An = {0}", an); } }
In Main scriem urmatorul cod pentru testare
class Program { static void Main(string[] args) { Info info = new Info(); info.PrintHostName(); InfoStructure infoStr = new InfoStructure(info, Environment.SystemDirectory); infoStr.PrintInfoStructure(); // sau InfoStructure creat fara a folosi operatorul new InfoStructure infos;
Pag. 14 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
infos.info = new Info(); infos.SystemDirectory = Environment.SystemDirectory; infos.an = 2013; infos.PrintInfoStructure(); Console.ReadLine(); } }
Pag. 15 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Tipuri referinta
Tipurile referinta sunt similare cu referintele din C++; pot fi considerati ca pointeri
siguri.
O referinta poate fi null. Cand referinta nu este null atunci puncteaza la obiectul de tipul
specificat si care a fost deja alocat in heap.
Observatie Referinta la un tip referinta este memorata pe stiva.
Exemplu:
System.String strString = "Hello, World";
Efect:
• s-a alocat memorie in heap;
• s-a copiat sirul de caractere “Hello, World”;
• se returneaza o referinta la aceasta valoare.
• referinta este memorata pe stiva. In continuare urmeaza o descriere a principalelor tipuri din CTS.
Tipuri din CTS – Common Type System Tipul class in C# O clasa este un tip definit de utilizator. Clasa este un tip referinta.
O clasa poate contine declaratii pentru urmatorii membri:
• Constructori
• Destructori
• Constante
• Campuri
• Metode
• Proprietati
• Indexers-i
• Operatori
• Evenimente
• Delegates
• Clase
• Interfete
• Structuri
Instanta unei clase se numeste obiect si se creaza folosind operatorul new. Clasele statice si
abstracte nu pot fi instantiate.
Pag. 16 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Observatie Destructorii se folosesc, in general, in clase ce contin resurse unmanaged si vor fi discutati la
cursul despre gestiunea memoriei - Garbage Collection. Destructorii sunt apelati de Garbage
Collector.
O clasa poate sa mosteneasca o singura clasa si poate implementa mai multe intefete, altfel
spus exista mostenire simpla la nivel de clase si mostenire multipla la nivel de interfete.
Sintaxa pentru definirea unei clase este (forma simplificata):
[atribut] [nivel_de_acces] [tip_clasa] class nume_clasa [:clasa_de_baza, Interfata_1, Interfata_2,...] { // corpul clasei }
unde:
atribut : Optional. Reprezinta un atribut (este o clasa derivata din System.Attribute). Vezi
cursul ce descrie atributele.
nivel_de_acces: stabileste "vizibilitatea" clasei. Valoarea implicita este internal - clasa
definita la nivel de namespace - sau private - clasa imbricata.
tip_clasa: Optional. In cazul cand se foloseste poate avea valorile:
abstract, new (folosit pentru clase imbricate), sealed, static.
Exemplu namespace Curs
{ class Persoana // echivalent internal class Persoana { // Date membru (campuri, variabile membru) public string nume; public int varsta; // ctor public Persoana(string nume, int varsta) { this.nume = nume; this.varsta = varsta; } // metoda a instantei public void Display() { Console.WriteLine("Nume: {0}, Varsta: {1}",
nume, varsta); } }
}
Crearea obiectului poate fi facuta astfel:
Persoana p = new Persoana("X", 12);
Pag. 17 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
sau
Persoana p; // nu se creaza obiectul. p are valoarea null. p = new Persoana("X", 12); sau Persoana p = new Persoana() { nume = “X”, varsta = 12 }
Observatie 1. Operatorul new poate fi folosit si pentru a crea tipuri valoare.
2. In C++ putem crea obiectele pe stiva printr-un apel de forma
Persoana p;
sau in heap folosind sintaxa :
Persoana p = new Persoana("X", 12);
In C++ trebuie sa avem grija sa dealocam din heap acest obiect (delete p;). In C# nu mai
trebuie sa facem acest lucru.
Orice clasa are cel putin un ctor. Daca nu-l definim noi, compilatorul va genera un ctor
implicit. Daca am declarat un ctor cu parametri, compilatorul nu va mai genera un ctor
implicit. In majoritatea cazurilor modificatorul de acces pentru ctor este public, dar aceasta nu
constituie o regula.
Ctor initializeaza starea obiectului. Daca avem un tip ce are o multime de date membru nu
suntem obligati sa trecem initializarea acestora in ctor. Ctor implicit inainte de a crea obiectul
in memorie se asigura ca toate datele membru au valori implicite. Ctor va atribui valori
implicite pentru tipurile valoare si valoarea null pentru tipurile referinta. De obicei valorile
numerice sunt initializate cu zero.
Exemplu de cod eronat (FxCop semnaleaza acest lucru, e doar un avertisment, nu o eroare in
adevaratul sens al cuvantului)
class Persoana { int varsta = 0 ; string nume ; public Persoana()
{ nume = “X” ; }
}
Data membru varsta este initializata de doua ori. A doua oara o face ctor.
Pag. 18 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Un tip poate avea definiti mai multi ctori. Diferenta este data de numarul parametrilor si de
tipul acestora.
Modificator de acces in
C# pentru tipuri Descriere
public Membrul este accesibil din afara definitiei clasei si a ierarhiei
claselor derivate.
protected Membrul nu este vizibil in afara clasei si poate fi accesat
numai de clasele derivate.
private Membrul nu este vizibil in afara clasei si nici in clasele
derivate.
internal
Membrul este vizibil numai in interiorul unitatii curente de
compilare, numai din fisierele din acelasi assembly. Acest
modificator creaza un hibrid intre public si protected, totul
depinzind in ultima instanta de locul unde se afla codul. O
utilizare comuna a acestui modificator este in dezvoltarea
bazata pe componente pentru ca permite unui grup de
componente sa coopereze intr-un mod privat.
protected internal * Accesul este limitat la assembly-ul curent sau la tipurile
derivate din clasa continuta.
Observatie :
La nivel de namespace un tip are modificatorul de acces internal sau public.
Clasele « imbricate » (« nested ») pot avea oricare din modificatorii de acces de mai sus.
Ex : namespace Info { class Persoana { ...} // implicit este internal public class Client { ... }
// Clasa de baza ce poate fi extinsa in procesul de derivare. // Contine cel putin o metoda sau proprietate ce trebuie // implementata de clasa derivata. // Nu poate fi instantiata.
public class abstract BazaInfo { ... }
// Nu poate fi folosita in procesul de derivare. public sealed class ClasaFinala { ... } } namespace InfoNested { class Persoana
{ private class Copil { ... } }
Pag. 19 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
public class Client { private class Banca { ... } public class Adresa { ... } protected class Comanda { ... } } // contine numai metode / date statice public static class Print { ... }
}
Modificatori de acces pentru date membru (fields) :
Membru in Accesibilitate
implicita
Permite modificarea accesibilitatii
enum public -
class
(aici class
apare ca data
membru in alta
clasa)
private public
protected
internal
private
protected internal
interface public -
struct private public
internal
private
In C# trebuie sa indicam pentru fiecare data membru modificatorul de acces.
Constructori. Inlantuire constructori. Cuvantul cheie this. public class Persoana { int varsta; string nume; public Persoana() : this(0,””)
{} public Persoana (int varsta) : this(varsta, “”) {} public Persoana (string nume) : this (0, nume); // constructorul general public Perosna(int varsta, string nume) { this.varsta = varsta; this.nume = nume; }
}
Pag. 20 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Metode statice, membri statici
Metodele statice nu pot lucra cu instante ale tipului. Se apeleaza furnizand numele clasei
urmat de . (punct) si apoi numele metodei.
public class Info { public static Print
{ Console.WtiteLine(“Metoda statica Print din clasa Info”); }
}
iar apelul este de forma
Info.Print() ;
Membri statici pot opera numai cu date statice si pot apela metode statice din tipul definit.
Campurile statice sunt create o singura data indiferent de cate instante ale tipului se vor crea.
Toate instantele create vor avea acceasi valoare pentru o data statica.
Intrebare :
Putem folosi un tip referinta ca fiind static in interiorul altui tip?
Raspunsul il gasim in urmatorul cod:
public class InfoStatic { public static Info info = new Info(); // metoda a instantei public void PrintInstanta() {
// in clasa Info de la exemplul anterior am declarat // HostName ca fiind public
Console.WriteLine("class InfoStatic. Metoda a instantei. Machine name = {0} ", info.HostName); info.PrintHostName(); } // metoda statica public static void PrintStatic() { Console.WriteLine("class InfoStatic. Metoda statica. {0} ",
info.HostName); } }
Testarea functionalitatii acestei clase se poate face astfel:
// test class InfoStatic, apel metoda statica
InfoStatic.PrintStatic(); // apel metoda a instantei InfoStatic infoStatic = new InfoStatic(); infoStatic.PrintInstanta();
Pag. 21 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Specificarea informatiei la run-time in lista de initializare a constructorului
Probleme legate de membrii statici si membrii instanta in initializarea constructorilor.
In C# numai membrii statici pot fi utilizati, in lista de initializare, cand se apeleaza
constructorii din clasa de baza. Mai exact, daca ctor din clasa derivata nu are parametri iar
ctor din clasa de baza are parametri atunci in lista de initializare trebuie specificati membri
statici.
Exemplu
class Baza { protected Baza(int i) { } }; class Derivata : Baza { int i;
// Eroare!!! Explicati! public Derivata() : base(i) { } };
Rezolvarea consta in a folosi o membri statici in lista de initializare a ctorului.
Constructor static
Reguli
• Clasa data (sau o structura) poate defini un singur ctor static.
• Ctor static nu are modificatori de acces si nu poate avea parametri.
• Un ctor static se executa o singura data, indiferent de cate obiecte s-au creat din tipul
respectiv.
• Runtime-ul invoca ctor static cand creaza o instanta a clasei sau inainte de a accesa
primul membru static invocat de apelant.
• Ctor static se executa inaintea oricarui ctor al instantei.
Exemplu class Bicicleta { public string model; public static int nRoti; static Bicicleta () { nRoti = 2; } public Bicicleta()
{ model = "necunoscut" ; }
}
Pag. 22 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Clase statice. Metode extinse.
Daca am definit o clasa ca fiind statica, aceasta nu poate fi instantiata cu operatorul new si
trebuie sa contina numai membri (date, metode) statici.
Totul se aloca pe stiva si ca urmare garbage collector nu are in vedere asemenea tipuri.
Unde sunt folosite ?
Vezi metodele extinse si nu numai.
Metodele extinse permit ca pentru tipurile compilate existente sa se adauge noi
functionalitati, fara a fi nevoiti sa rescriem tipul. Metodele extinse se aplica si pentru clasele
« sealed » (o clasa « sealed » nu poate fi utilizata in procesul de derivare).
Aceasta tehnica adauga noi metode pentru un tip in contextul aplicatiei curente.
Analizati cu atentie exemplul de mai jos.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; namespace ExtensionMethod {
/// <summary> /// Metode extinse pentru tipul System.Int32 /// 1. Clasa trebuie sa fie statica /// 2. Metoda trebuie sa fie statica /// 3. Primul parametru al metodei this urmat de tipul pe care /// se va aplica metoda /// </summary>
public static class MetodeExtinse {
// Metoda permite unui obiect sa afiseze assembly // in care este definit
public static void AfisareAssemblyGazda(this object obj) { Console.WriteLine("{0} Este definit in:\n\t->{1}\n", obj.GetType().Name, Assembly.GetAssembly(obj.GetType())); } // Inverseaza cifrele unui numar intreg (Ex. 13 -> 31) // Metoda extinsa pentru tipul int public static int InversareIntreg(this System.Int32 i) { // Transform intregul in string si apoi
// iau toate caracterele char[] digits = i.ToString().ToCharArray(); // Inversez articolele in array Array.Reverse(digits);
Pag. 23 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
// Construiesc un nou string string newDigits = new string(digits); // Conversie la intreg si return return int.Parse(newDigits); } // metoda extinsa pentru tipul int public static int Aduna(this System.Int32 n, int m) { return n + m; } } // Test class Program { static void Main(string[] args) { int Index = 12; int n = 13; // apel metoda extinsa int ns = n.InversareIntreg(); Console.WriteLine("Intreg initial = {0},
si inversat = {1}",n, ns); // apel metoda extinsa ce aduna doua numere intregi n = Index.Aduna(n); // n = n + Index Console.WriteLine("Adunare : " + n.ToString()); // apel metoda extinsa pe tipul object object o = n; o.AfisareAssemblyGazda(); } } }
Desi metodele AfisareAssemblyGazda, Aduna, InversareIntreg au fost definite ca
fiind statice in cadrul unei clase statice, ele se apeleaza pe o instanta a tipului. Analizati
prototipurile metodelor extinse si modul de apel.
Ce observati ?
Primul parametru al metodei extinse indica tipul pentru care se aplica acea metoda.
Pag. 24 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Metode ale instantei. Metode statice.
Metodele sunt totdeauna definite in interiorul clasei sau structurii.
Metodele pot fi:
• instance (apelata ca o instanta a tipului in interiorul caruia metoda a fost definita)
sau
• static, unde metoda este asociata cu tipul insusi.
Metodele pot fi declarate ca virtual, abstract sau sealed.
Metodele pot fi supraincarcate (overloaded), suprascrise (overriden) si/sau ascunse
(hidden – vezi operatorul new).
Metodele instantei sunt apelate pe instanta obiectului.
Metodele statice nu sunt apelate pe instanta clasei ci prin prefixarea numelui metodei cu
numele clasei unde este definita.
Exercitiu
Presupunem ca implementam un tip ce contine o data membru statica.
Este posibil sa folosim aceasta data membru statica intr-o metoda a instantei tipului ?
Cuvantul cheie base
Scenariu
Presupunem ca am construit o clasa ce poate fi mostenita si furnizeaza mai multi
ctori. Folosim aceasta clasa intr-un proces de derivare iar clasa derivata
implementeaza mai multi ctori.
Intrebare :
Ce constructor se va apela din clasa de baza in momentul cand instantiem clasa derivata ?
base este folosit pentru apelul constructorilor din clasa de baza. Acesta se va folosi in
constructorul clasei derivate pentru a indica ce constructor va fi apelat - din clasa de baza.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Exemplu { public class Baza { int n; string nume; public Baza(int _n) { Console.WriteLine("Baza ctor param int"); this.n = _n; } public Baza(int _n, string _str) :this(_n) {
Pag. 25 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
//this.n = _n; Console.WriteLine("Baza ctor param int si string"); this.nume = _str; } // alte metode ... }
public class Derivata : Baza { int x; public Derivata(int _x) : base(_x) { Console.WriteLine("Derivata ctor"); this.x = _x; } public Derivata(int _x, string s) : base(_x, s) { this.x = _x; Console.WriteLine("Derivata ctor param int, string"); } // alte metode ... } }
Supraincarcarea operatorilor (overloading)
Supraincarcarea operatorilor permite implementari ale operatorilor, implementari definite de
utilizator, pentru operatii unde unul sau ambii operanzi sunt tipuri definite de utilizator, tipuri
class sau struct.
Operatori supraincarcabili (MSDN)
Operators Overloadability
+, -, !, ~, ++, --, true, false These unary operators can be overloaded.
+, -, *, /, %, &, |, ^, <<, >> These binary operators can be overloaded.
==, !=, <, >, <=, >= The comparison operators can be overloaded
(but see note below).
&&, || The conditional logical operators cannot be
overloaded, but they are evaluated
using & and |, which can be overloaded;
see 7.11.2 User-defined conditional logical
operators.
[ ] The array indexing operator cannot be
overloaded, but you can define indexers.
( ) The cast operator cannot be overloaded, but you
Pag. 26 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
can define new conversion operators
(see explicit andimplicit).
+=, -
=, *=, /=, %=, &=, |=, ^=, <<=, >>=
Assignment operators cannot be overloaded,
but +=, for example, is evaluated using +, which
can be overloaded.
=, ., ?:, ->, new, is, sizeof, typeof These operators cannot be overloaded.
Observatie Operatorii de comparatie, daca sunt supraincarcati, trebuie supraincarcati in pereche. Daca ==
este supraincarcat, atunci trebuie supraincart si !=.
Exemplu (MSDN)
// complex.cs
using System;
public struct Complex
{
public int real;
public int imaginary;
public Complex(int real, int imaginary)
{
this.real = real;
this.imaginary = imaginary;
}
public static Complex operator +(Complex c1, Complex c2)
{
return new Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
}
Utilizare
Complex num1 = new Complex(2,3);
Complex num2 = new Complex(3,4);
Complex sum = num1 + num2;
Definire operatori pentru conversie
Se pot folosi operatori de conversie implicit sau explicit. Conversia explicita obliga la utilizare
cast.
Exemplu (MSDN) pentru conversie explicita
struct Digit
{
Pag. 27 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
byte value;
public Digit(byte value) //constructor
{
if (value > 9)
{
throw new System.ArgumentException();
}
this.value = value;
}
public static explicit operator Digit(byte b)
// explicit byte to digit conversion operator
{
Digit d = new Digit(b); // explicit conversion
System.Console.WriteLine("Conversion occurred.");
return d;
}
}
class TestExplicitConversion
{
static void Main()
{
try
{
byte b = 3;
Digit d = (Digit)b; // explicit conversion
}
catch (System.Exception e)
{
System.Console.WriteLine("{0} Exception caught.", e);
}
}
}
Exemplu (MSDN) conversie implicita
struct Digit
{
byte value;
public Digit(byte value) //constructor
{
if (value > 9)
{
throw new System.ArgumentException();
}
this.value = value;
}
Pag. 28 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
public static implicit operator byte(Digit d)
// implicit digit to byte conversion operator
{
System.Console.WriteLine("conversion occurred");
return d.value; // implicit conversion
}
}
class TestImplicitConversion
{
static void Main()
{
Digit d = new Digit(3);
byte b = d; // implicit conversion -- no cast needed
}
}
Pag. 29 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Tipuri din CTS – Common Type System
Tipul structura - struct
Este asemanator cu cel din C. Layout-ul de memorie este diferit.
Compatibilitate cu structurile din C daca se foloseste atributul [StructLayout(Sequential)].
Structurile pot contine campuri si metode ce opereaza pe aceste date.
Structurile pot defini constructori, pot implementa interfete si pot contine orice numar de
proprietati, metode, evenimente si operatori supraincarcati.
Cuvantul cheie folosit in C# este struct.
struct Punct { public float x; public float y; // Constructor public Punct( float _x, float _y)
{ x = _x; y = _y; } // Metode public void Display() { Console.WriteLine(“ (x = {0}, y = {1}) “, x, y); }
}
Revedeti si exemplul cu InfoStructure prezentat la inceputul cursului.
Observatii:
� Campurile pot fi initializate numai daca sunt declarate const sau static.
Structura nu poate sa declare un ctor implicit.
� Structurile pot declara ctor cu parametri.
� Structura nu poate fi folosita in procesul de derivare.
� Structurile sunt copiate la atribuire.
� Sunt tipuri valoare.
� Structura poate implementa interfete.
� In C# struct este diferit de class.
� In C++ struct = public class.
Pag. 30 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Tipuri din CTS – Common Type System
Tipul enumerare - enum
Enumerarile ne permit sa grupam o pereche de nume/valoare.
Cuvantul rezervat in C# este enum.
enum Stare { Valid = 1, Modificat = 2, Sters = 3 }
Tipul delegate din CTS - delegate
Delegates sunt echivalentul in .NET pentru pointeri la functii din C.
Delegate este o clasa derivata din System.MulticastDelegate.
In C# cuvantul cheie folosit este delegate.
Ex delegate bool EsteNumarPrim(int n);
Acest delegate poate puncta la orice metoda ce returneaza un bool si are ca parametru de
intrare un int.
Pentru exemplul dat, prototipul metodei este :
public bool EstePrim(int n) ;
Delegates in .NET constituie baza arhitecturii bazata pe evenimente si permit apel de metode
in mod asincron.
Observatie:
La intalnirea unei asemnea declaratii, compilatorul de C# va genera o clasa derivata din
System.MulticastDelegate. Mai multe detalii in cursul despre delegates.
Tipul “membri” din CTS
Un tip membru este un element al multimii
{constructor, finalizer, // destructor in C# static constructor, nested type – tip imbricat, operator, method, property, indexer, field, read-only field,
Pag. 31 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
constant, event}.
Fiecare membru are o anumita vizibiltate
(public, private, protected, internal, protected internal). Anumiti
membri pot fi declarati ca fiind abstract sau virtual sau static.
Tipul de data preconstruit (“intrinsec”) din CTS – tip VALOARE Tip Data in CTS VB .NET C# C++/CLI Keyword
System.Byte Byte byte unsigned char System.SByte SByte sbyte signed char System.Int16 Short short short System.Int32 Integer int int or long System.Int64 Long long __int64 System.UInt16 UShort ushort unsigned short System.UInt32 UInteger uint unsigned int or unsigned long System.UInt64 ULong ulong unsigned __int64 System.Single Single float Float System.Double Double double Double System.Object Object object Object^ System.Char Char char wchar_t System.String String string String^ System.Decimal Decimal decimal Decimal System.Boolean Boolean bool Bool
Metode cu si fara parametri
ref ca parametri ai metodei
Cuvantul cheie ref spune compilatorului C# ca argumentele pasate puncteaza la aceeasi
memorie ca si variabilele din codul apelant. Daca metoda apelata modifica valorile,
modificarile sunt vazute in codul apelant.
Restrictie:
Cand folosim ref, trebuie sa initializam argumentele inainte de a le folosi ca argumente ale
functiei. Exemplu:
class Color { public Color() { this.red = 0; this.green = 127; this.blue = 255; } protected int red; protected int green; protected int blue;
Pag. 32 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
public void GetRGB( ref int red, ref int green, ref int blue) { red = this.red; green = this.green; blue = this.blue; } }
class TestRef { static void Main(string[] args) { Color c = new Color(); // Initializare int red = 0; int green = 0; int blue = 0; c.GetRGB(ref red, ref green, ref blue); Console.WriteLine("R={0}, G={1}, B={2}", red, green, blue); } }
out ca parametri ai metodei
Are acelasi efect ca si ref, dar diferenta principala este ca nu e necesara initializarea
parametrilor inainte de apel.
Parametrii prefixati cu out trebuiesc modificati in metoda apelata, iar parametrii prefixati cu
ref pot fi modificati.
Exemplu: class Color { public Color() { this.red = 0; this.green = 127; this.blue = 255; } protected int red; protected int green; protected int blue; public void GetRGB(out int red, out int green, out int blue) { red = this.red; green = this.green; blue = this.blue;
Pag. 33 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
} }
class TestOut { static void Main(string[] args) { Color c = new Color(); int red; int green; int blue; c.GetRGB(out red, out green, out blue); Console.WriteLine("R={0}, G={1}, B={2}", red, green, blue); } }
Pag. 34 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Pasarea unui tip referinta prin valoare (parametru al metodei)
Daca o variabila tip referinta este un argument al unei functii, atunci se poate modifica
continutul variabilei, adica starea obiectului dar nu si obiectul.
Intrebare :
Se poate modifica obiectul cand acesta este transmis ca parametru al unei functii?
vezi ex C1_2009 din d:\exenet
De comentat liniile ce contin apel prin referinta
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace C1_2009 { class Mate { public int Aduna(int x, int y) { return x + y; } public double Aduna(float x, float y) { return x + y; } public string Aduna(double x, double y, string text) { return " x + y = " + (x + y).ToString() +
" parametrul 3 este : " + text; } } class Persoana { public string nume; public int varsta; public Persoana(string nume, int varsta) { this.nume = nume; this.varsta = varsta; } public void Display() { Console.WriteLine("Nume: {0}, Varsta: {1}",
nume, varsta); } } class Program { static void Main(string[] args) { Mate m = new Mate();
Pag. 35 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Console.WriteLine(" 1 + 2 = {0} ", m.Aduna(1, 2)); Console.WriteLine(" 1.5 + 2.0 = {0}", m.Aduna(1.5F, 2.0F)); Console.WriteLine(" 1.5 + 2.0 = {0} ", m.Aduna(1.5, 2.0, "Aduna cu trei parametri")); Console.WriteLine("\n\nTest Persoana parametru prin valoare"); Persoana p = new Persoana("Elena", 21); p.Display(); TransmitePersoanaPrinValoare(p); p.Display(); Console.ReadLine(); } static void TransmitePersoanaPrinValoare(Persoana p) { // Schimbam varsta? p.varsta = 99; // Apelantul va vedea aceasta reatribuire? // Cream un obiect nou p = new Persoana("Vasile", 99); } } }
Ce se intampla?
Pasarea unui tip referinta prin referinta
vezi ex C1_2009 din d:\exenet
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace C1_2009 { class Mate { public int Aduna(int x, int y) { return x + y; } public double Aduna(float x, float y) { return x + y; } public string Aduna(double x, double y, string text) { return " x + y = " + (x + y).ToString() +
" parametrul 3 este : " + text; } } class Persoana { public string nume; public int varsta;
Pag. 36 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
public Persoana(string nume, int varsta) { this.nume = nume; this.varsta = varsta; } public void Display() { Console.WriteLine("Nume: {0}, Varsta: {1}",
nume, varsta); } } // test class Program { static void Main(string[] args) { Mate m = new Mate(); Console.WriteLine(" 1 + 2 = {0} ", m.Aduna(1, 2)); Console.WriteLine(" 1.5 + 2.0 = {0}", m.Aduna(1.5F, 2.0F)); Console.WriteLine(" 1.5 + 2.0 = {0} ",
m.Aduna(1.5, 2.0, "Aduna cu trei parametri")); Console.WriteLine(
"\n\nTest Persoana parametru prin valoare"); Persoana p = new Persoana("Elena", 21); p.Display(); TransmitePersoanaPrinValoare(p); p.Display(); Console.WriteLine(
"\n\nTest Persoana parametru prin referinta"); p.varsta = 21; p.Display(); TransmitePersoanaPrinReferinta(ref p); p.Display(); Console.ReadLine(); } static void TransmitePersoanaPrinValoare(Persoana p) { // Schimbam varsta? p.varsta = 99; // Apelantul va vedea aceasta reatribuire? p = new Persoana("Vasile", 99); } static void TransmitePersoanaPrinReferinta(ref Persoana p) { // Schimbam varsta? p.varsta = 99; // Apelantul va vedea aceasta reatribuire? p = new Persoana("Vasile", 99); } } }
Pag. 37 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Care este rezultatul?
Discutie
Reguli (de aur!):
• Daca un tip referinta este pasat prin referinta, apelatul poate schimba valoarea
starii obiectului (datele) cat si obiectul la care se face referire – se poate returna
un nou obiect.
• Daca un tip referinta este pasat prin valoare, apelatul poate schimba starea
datelor obiectului dar nu si obiectul la care se face referire.
Rezumat tip valoare si tip referinta
Intrebare Tip valoare Tip referinta
Unde este tipul alocat? Pe stiva In managed heap.
Cum este reprezentata
variabila?
Copii locale Puncteaza la memoria
alocata instantei
Care este tipul de baza ? System.ValueType Derivat din orice ce nu e
ValueType sau sealed.
Poate acest tip sa fie baza
pentru alte tipuri ?
Nu Da, daca nu e sealed.
Care e tipul implicit pentru
pasarea parametrilor?
Valoare. Se trimite o copie. Referinta.
Poate acest tip sa suprascrie
(override) metoda
System.Object.Finalize()?
Nu. Tipurile valoare nu fac
obiectul procesului de
garbage collection, sunt
alocate pe stiva.
Da. Indirect prin destructor.
Pot defini ctori pt acest tip ? Da. Numai ctor cu
parametru, ctor implicit
este rezervat.
DA!
Cand sunt distruse aceste
variabile ?
Cand sunt in afara blocului
unde au fost definite.
Cand nu mai e nevoie le
distruge garbage collector
(mod nedeterminist).
Tipul Nullable – se aplica numai tipului valoare
Nullable<bool> bOk = null ; // corect bool ? bOK = null ; // sintaxa prescurtata
Testul se face pe null. Vezi si proprietatile HasValue si Value.
// Citeste int din baza de date. int? i = dr.GetIntFromDatabase(); if (i.HasValue)
Console.WriteLine("Valoarea lui 'i' este: {0}", i.Value); else
Console.WriteLine("Valoarea lui 'i' este nedefinita."); // Citeste bool din baza de date. bool? b = dr.GetBoolFromDatabase(); if (b != null)
Pag. 38 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Console.WriteLine("Valoare lui 'b' este: {0}", b.Value); else
Console.WriteLine("Valoarea lui 'b' este nedefinita.");
Prototipul pentru
public int? GetIntFromDatabase() {
return valoare; /* valoare trebuie sa fi fost
definta ca fiind Nullable */ }
Operatorul ??
ne permite sa atribuim o valoare pentru un tip declarat ca Nullable daca valoarea
acestuia este null.
Ex.
int? data = dr.GetIntFromDatabase() ?? 100;
Daca valoarea gasita este null se va returna valoarea 100.
Proprietati
Proprietatile nu permit accesul direct la membri, la campurile unde sunt memorate in fapt
datele. Sunt furnizati doi accesori (set, get) ce lucreaza direct cu data membru. Scopul este de
a pastra date cit mai corecte in obiectul instantiat.
• Proprietati clasice – au nevoie de camp suplimentar pentru memorare valoare.
• Pproprietati automate – nu au nevoie de camp suplimentar pentru memorare valoare.
Definirea si folosirea proprietatilor
O propritate in C# consta din declararea unui camp si a unui accesor folosit pentru a-i
modifica valoarea. Accesorii sunt referiti ca metode setter si getter.
[attributes] [modifers] <type> <property-name>{ [ set { <accessor-body> } ] [ get { <accessor-body >} ] }
Observatii
Nu e nevoie sa definim ambii accesori.
• get face proprietatea read
• set face proprietatea write
• proprietatea nu poate fi utilizata ca parametru intr-o metoda.
Pag. 39 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
• modificatorul static poate fi utilizat pentru proprietate.
Modificatorii de acces trebuiesc specificati la nivel de proprietate.
Proprietati automate : obligatoriu trebuie declarati ambii accesori get/set.
get / set pot avea modificatori de acces.
public int Intreg { get ; private set ;}
Proprietatea Intreg va fi setata in cadrul clasei. In afara clasei Intreg apare ca Read Only.
Nu mai trebuie sa declaram un camp suplimentar in cadrul clasei :
public class Persoana {
public string Name {get ; set ;} }
Dezavantaj: nu putem scrie cod pentru get / set.
Nou C# 6.0 public class Persoana
{
/* Getter only property with inline initialization */
public string Name { get; } = "Popescu"
/* Property with inline initialization */
public decimal Salar { get; set; } = 12345;
}
Exista deosebiri intre proprietati automate si campuri publice din clasa?
• Campurile (fields) pot fi folosite ca argumente cu ref/out. Proprietatile nu pot fi
folosite in acest mod.
• Un camp va furniza aceeasi valoare cand este apelat de mai multe ori (exceptie fire de
executie). O proprietate cum ar fi DateTime.Now furnizeaza valori diferite.
• Proprietatile pot genera exceptii, campurile nu.
• Proprietatile pot produce efecte secundare sau timpul de executie este mult mai mare.
Campurile nu – cu conditia sa fie folosite corect.
• Proprietatile suporta accesibilitati diferite, campurile nu. Campurile pot fi facute
readonly.
• Introspectia metadatei pentru proprietati se face cu metoda GetProperties, iar
pentru campuri cu metoda GetFields.
• Proprietatile pot fi folosite in interfete, campurile nu.
interface IPerson { string FirstName { get; set; } string LastName { get; set; } }
Pag. 40 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Exemplu de folosire interfata (implementare):
class Person: IPerson {
private string _name;
public string FirstName {
get {
return _name ?? string.Empty;
} set {
if (value == null) throw new System.ArgumentNullException("value"); _name = value;
} } ...
}
Evenimente
Evenimentele permit unei clase sau unui obiect de a notifica alte clase sau obiecte cand s-a
intamplat ceva in cadrul obiectului. Clasa ce trimite evenimentul se numeste publisher si clasa
ce primeste (sau trateaza evenimentul, raspunde la eveniment) se numeste subscriber.
Evenimentele au urmatoarele proprietati:
� Obiectul publisher deteremina cand se genereaza evenimentul; subscriber-ul determina
ce actiune se va executa la aparitia evenimentului.
� Un eveniment poate avea mai multi subscriber-i, adica mai multe clase pot declara
metode ce trateaza acel eveniment. In acest caz metodele ce trateaza evenimentul sunt
apelate in mod sincron.
� Un subscriber poate trata mai multe evenimente de la publisher-i diferiti.
� Evenimentele ce nu au subscriber nu sunt generate.
Toate evenimentele din FCL sunt bazate pe delegate EventHandler, ce este definit astfel:
public delegate void EventHandler(object sender, EventArgs e);
.NET Framework 2.0 defineste o versiune generica a acestui delegate, EventHandler<TEventArgs>.
Utilizare pattern EventHandler
1. Stabilire parametri pentru eveniment. Clasa trebuie derivata din
System.EventsArgs. Se declara proprietati in aceasta clasa, proprietati ce sunt
vizibile in “publisher” si in “subscriber”. In ex. de mai jos proprietatea este Message.
public class CustomEventArgs : EventArgs {
Pag. 41 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
public CustomEventArgs(string s) { msg = s; } private string msg; public string Message { get { return msg; } } }
2. Stabilire delegate.
a. Daca folosim versiunea generica EventHandler<TEventArgs> nu
avem nimic de declarat.
b. In caz contrar definim delegate-ul (in ex se numeste CustomDelegate)
vizibil in publisher:
public delegate void CustomEventHandler( object sender, CustomEventArgs a);
3. Declaram evenimentul in clasa publisher.
3.1 In cazul (2.a, versiunea non-generica) declaratia este:
public event EventHandler RaiseCustomEvent;
In cazul (2.a, versiunea generica) declaratia este:
public event EventHandler<CustomEventArgs> RaiseCustomEvent;
3.2 In cazul (2.b) declaratia este:
public event CustomEventHandler RaiseCustomEvent;
4. In clasa publisher trebuie definita o metoda ce va face posibil apelul subscriber-ilor.
Aceasta metoda se apeleaza din cadrul altei metode din aceasta clasa, altfel spus se
genereaza evenimentul. Codul cel mai simplu ar putea fi: protected virtual void OnRaiseEvent(// parametri) {
// cod stabilire valori pentru sender si paramsEvent // ... // Cazul generic. Decomentati linia de mai jos.
// EventHandler<CustomEventArgs> handler = RaiseCustomEvent;
// Cazul non-generic. Decomentati linia de mai jos // CustomEventHandler handler = RaiseCustomEvent;
if (handler != null)
handler(sender, paramsEvent); }
Observatie
Pag. 42 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Aceasta metoda va fi apelata de alte metode din clasa publisher.
In clasa subscriber va trebui sa ne inscriem pentru acest eveniment astfel:
class Subscriber
{
private string id;
public Subscriber(string ID, Publisher pub)
{
id = ID;
// Atasare metoda la evenimentul RaiseCustomEvent
// Inscriere pentru eveniment
pub.RaiseCustomEvent += HandleCustomEvent;
}
// Definire metoda HandleCustomEvent
void HandleCustomEvent(object sender, CustomEventArgs e)
{
// cod ...
}
}
Boxing si Unboxing
Conversii
Boxing = conversia de la tip valoare la tip referinta.
Unboxing = conversia de la tip referinta la tip valoare.
Conversia din tip valoare la tip referinta (boxing)
Se creaza o copie noua a obiectului ce va fi supus conversiei. Exemplu int nVarsta = 22; // tip valoare object refVarsta = nVarsta; // nVarsta este convertit la refVarsta // nu e nevoie de o conversie explicita
1. se aloca memorie in heap, atat pentru a mentine valoarea obiectului cat si pentru a
mentine starea acestuia (metode, structuri interne, tabela de metode virtuale).
2. Se copie valoarea la noua adresa.
3. Adresa obiectului nou alocat este plasata pe stiva si acum puncteaza la un obiect
referinta.
Conversia din tip Referinta la tip valoare (unboxing)
Nu se creaza o noua copie a obiectului supus conversiei.
In acest caz conversia poate fi facuta la orice tip. Se aplica reguli stricte definite in CTS.
Exemplu:
Pag. 43 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
int nVarsta = 22; // tip valoare. object refVarsta = nVarsta; // (boxed) refVarsta puncteaza la nVarsta int nAlt = (int)refVarsta; // (Unboxed) inapoi la int. // este nevoie de o conversie explicita
Sa urmarim urmatorul cod si apoi sa vedem ce este in CIL. …
int n1 = 32; object refn1 = n1; // boxing Console.WriteLine("Initial n1 = {0}", n1, refn1); Console.WriteLine("Initial refn1 = {0}", refn1); n1 = (int) refn1; // unboxing n1 = 33; Console.WriteLine("Dupa Initial n1 = {0},
refn1 = {1}", n1, refn1); … In CIL avem: .method private hidebysig static void Main(string[] args) cil managed { .entrypoint .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) // Code size 73 (0x49) .maxstack 3 .locals init ([0] int32 n1, [1] object refn1) IL_0000: ldc.i4.s 32 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: box [mscorlib]System.Int32 IL_0009: stloc.1 IL_000a: ldstr "Initial n1 = {0}" IL_000f: ldloc.0 IL_0010: box [mscorlib]System.Int32 IL_0015: ldloc.1 IL_0016: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_001b: ldstr "Initial refn1 = {0}" IL_0020: ldloc.1 IL_0021: call void [mscorlib]System.Console::WriteLine(string, object) IL_0026: ldloc.1 IL_0027: unbox [mscorlib]System.Int32 IL_002c: ldind.i4 IL_002d: stloc.0 IL_002e: ldc.i4.s 33 IL_0030: stloc.0 IL_0031: ldstr "Dupa Initial n1 = {0}, refn1 = {1}" IL_0036: ldloc.0
Pag. 44 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
IL_0037: box [mscorlib]System.Int32 IL_003c: ldloc.1 IL_003d: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_0042: call string [mscorlib]System.Console::ReadLine() IL_0047: pop IL_0048: ret } // end of method Test::Main
Mostenire si polimorfism
Mostenirea faciliteaza reutilizarea codului.
Reutilizarea codului poate fi facuta in doua moduri :
• mostenire clasica ce stabileste o relatie de « este un » - « is-a » ;
• containment / agregare ce stabileste o relatie de « are un » - « has-a ».
Cand stabilim o relatie « is-a » intre clase, construim de fapt o dependenta intre doua sau mai
multe clase. Ideea este ca noua clasa poate extinde functionalitatea clasei existente (clasa de
baza).
Daca o clasa este declarata « sealed » atunci aceasta nu poate fi folosita drept clasa de baza
pentru o alta clasa (clasa “sealed” nu poate fi extinsa prin procesul de mostenire).
In .NET exista mostenire simpla a claselor si mostenire multipla a interfetelor.
In cazul polimorfismului vom folosi cuvintele cheie virtual si override pentru metoda in
cauza.
• virtual se foloseste in clasa de baza.
• override se foloseste in clasa derivata.
Observatie Functia suprascrisa trebuie sa aiba acelasi nivel de acces ca si functia virtuala pe care o
suprascrie.
Un membru virtual nu poate fi declarat private.
Exemplu:
class Persoana { public string name; public Persoana(string name) { this.name = name; } public virtual void Calcul() { Console.WriteLine( "Persoana.Calcul apelata pentru {0}", name);
Pag. 45 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
} } class ContractPersoana : Persoana { public ContractPersoana(string name) : base(name) { } public override void Calcul() { Console.WriteLine( "ContractPersoana.Calcul apelata pentru {0}", name); } } class Salariat : Persoana { public Salariat (string name) : base(name) { } public override void Calcul() { Console.WriteLine( "Salariat.Calcul apelata pentru {0}", name); } } class TestPolimorfism { protected Persoana[] persoane; public void LoadPersoane() { // Simulare incarcare din baza de date persoane = new Persoana[2]; persoane[0] = new ContractPersoana("ABBA"); persoane[1] = new Salariat("Evora"); } public void DoCalcul() { for (int i = 0; i < persoane.GetLength(0); i++) { persoane[i].Calcul(); } } static void Main(string[] args) { TestPolimorfism t = new TestPolimorfism(); t.LoadPersoane(); t.DoCalcul(); } }
Pag. 46 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Metode: combinatie new si virtual Putem combina new si virtual cand declaram o functie.
Pentru ca este new ea ascunde orice functie mostenita cu aceeasi semnatura.
Pentru ca este virtual ea poate fi suprascrisa in clasele derivate.
Exemplu class Persoana { public string name; public Persoana(string name) { this.name = name; } public virtual void Calcul() { Console.WriteLine( "Persoana.Calcul apelata pentru {0}", name); } } class Salariat : Persoana { public Salariat (string name) : base(name) { } public new virtual void Calcul() { Console.WriteLine( "Salariat.Calcul apelata pentru {0}", name); } } class ContractPersoana : Salariat { public ContractPersoana(string name) : base(name) { } public override void Calcul() { Console.WriteLine( "ContractPersoana.Calcul apelata pentru {0}", name); } } class Test { protected Persoana[] persoane; public void LoadPersoane()
Pag. 47 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
{ persoane = new Persoana[2]; persoane[0] = new ContractPersoana("ABBA"); persoane[1] = new Salariat("Evora"); } public void DoCalcul() { for (int i = 0; i < persoane.GetLength(0); i++) { persoane[i].Calcul(); } } static void Main(string[] args) { Test t = new Test(); t.LoadPersoane(); t.DoCalcul(); } }
Rezultatul va fi:
?
Urmatoarele doua declaratii sunt identice: public new virtual void Calcul() //public virtual void Calcul()
Implementare polimorfism folosind clase abstracte
In acest caz in clasa de baza se declara metoda ca fiind abstract iar in clasa derivata
override.
Exemplu (Troelsen):
// Clasa de baza abstracta abstract class Shape {
public Shape() {} public Shape(string name = "NoName")
{ PetName = name; } public string PetName { get; set; } // Metoda abstracta. Se forteaza scrierea codului // in clasa derivata – in caz contrar clasa derivata // trebuie declarata abstracta public abstract void Draw();
}
Clasa derivata: class Circle : Shape {
public Circle() {}
Pag. 48 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
public Circle(string name) : base(name) {} public override void Draw() {
Console.WriteLine("Drawing {0} the Circle", PetName); }
} class Hexagon : Shape {
public Hexagon() {} public Hexagon(string name) : base(name) {} public override void Draw() {
Console.WriteLine("Drawing {0} the Hexagon", PetName); }
}
in Main putem scrie codul: static void Main(string[] args) {
Console.WriteLine("***** Polymorphism *****\n"); // Construim un array de obiecte Shape compatibile. Shape[] myShapes = {
new Hexagon(), new Circle(), new Hexagon("H1"), new Circle("C"), new Hexagon("H2")};
// Iterare pe articolele din array si apelul metodei Draw(). foreach (Shape s in myShapes) {
s.Draw(); } Console.ReadLine();
}
Rezultatul va fi: ***** Polymorphism ***** Drawing NoName the Hexagon Drawing NoName the Circle Drawing H1 the Hexagon Drawing C the Circle Drawing H2 the Hexagon
Suprascrierea proprietatilor mostenite
Proprietatea trebuie specificata ca fiind virtual in clasa de baza pentru a o suprascrie, iar in
clasa derivata override..
using System; class FlatFile { public FlatFile(string fileName) { this.fileName = fileName; }
Pag. 49 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
protected string fileName; public virtual string FileName { get { return fileName; } } } class FlatTable : FlatFile { public const string FILENAME = "flatfile.txt"; public FlatTable() : base(FILENAME) {} public override string FileName { get { return "Flat table"; } } }
Clasa abstracta ce contine o proprietate abstracta
abstract class AbstractBaseClass { public abstract double AbstractProperty { get; set; } }
In exemplul urmator este descrisa suprascrierea proprietatilor mostenite (proprietatile sunt
declarate abstract). Acelasi mecanism: abstract in clasa de baza si override in clasa derivata.
using System; using System.Collections; abstract class Persoana { protected Persoana(int Id, int ore) { this.Id = Id; OreLucrate = ore; } protected int Id; public int PersoanaId { get { return Id; } } protected int OreLucrate; protected double costOra = -1; // initializare public abstract double CostOra { get; } }
Pag. 50 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
class ContractPersoana : Persoana { public ContractPersoana(int Id, double _ore, int _oreLucrate) : base(Id, _oreLucrate) { costOra = _ore; } protected double costOra; public override double CostOra { get { return costOra; } } }
Pag. 51 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
In acest curs am abordat urmatoarele teme:
Arhitectura .NET Framework
� CLR – Common Language Runtime
o CTS – Common Type System
o CLS – Common Language Specification
� BCL (FCL) – Base Class Library (Framework Class
Library)
o Tipuri de aplicatii ce pot fi dezvoltate sub aceasta platforma
o Spatii de nume
� Trasaturi principale ale limbajuui C#.
� Tipuri
o Tip Valoare o Tip Referinta
� Metode
o ale instantei o statice
� Modificatori de acces pentru tip.
� Constructori. Constructori statici.
� Clase statice. Metode extinse.
� Proprietati. Delegates. Evenimente.
� Mostenire.
� Polimorfism.
Pag. 52 din 52
Arhitectura Framework .NET
Ioan Asiminoaei email: [email protected]
Probleme propuse:
Observatie:
Problemele se considera complet rezolvate daca se prezinta si codul ce testeaza
implementarile.
1. Implementati polimorfismul folosind clase de baza abstracte
Enunt problema: C1_P1
Trebuie sa construim mai multe clase ce parteajeaza proprietati comune, metode, evenimente,
indexeri; implementarile pot fi diferite pentru fiecare clasa. Aceste clase nu trebuie sa
partajeze numai cod comun dar sa fie si de natura polimorfica, altfel spus codul ce foloseste
un obiect al clasei de baza ar trebui sa fie in stare sa foloseasca un obiect al oricarei clase
derivate din clasa de baza in acelasi mod.
2. Implementati polimorfismul folosind interfete
Enunt problema : C1_P2
Trebuie sa implementati functionalitatea polimorfica pe o multime de clase existente. Aceste
clase sunt deja derivate dintr-o clasa de baza (alta decat Object), si nu puteti adauga
functionalitatea polimorfica folosind clase de baza abstracte sau concrete. In plus trebuie sa
adaugati functionalitate polimorfica la o structura.
Observatie :
Clasele de baza abstracte sau concrete nu pot fi utilizate in acest din urma caz.
Indicatie:
Puteti considera urmatoarele clase (T este un tip generic): public class Comanda<T> : List<T> { // … } public class Client<T> : List<T> { // … }
Doriti sa adaugati posibilitatea de a afisa fiecare din cele doua obiecte in mod polimorfic.
Pentru aceasta creati interfata IPrint ce defineste metoda Print care va fi implementata de
cele doua clase. public interface IPrint { void Print( ); }
Top Related