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.
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)
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.
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.
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.
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.
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.
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()
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.
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.
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 ;
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.
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;
Ioan Asiminoaei email: [email protected] infos.info = new Info();
infos.SystemDirectory = Environment.SystemDirectory;
infos.an = 2013;
infos.PrintInfoStructure();
Console.ReadLine();
} }
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.
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);
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.
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 { ... } }
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;
} }
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();
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" ; }
}
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);
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.
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)
{
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
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
{
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;
}
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 }
}
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.
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,
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;
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;
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);
} }
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();
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;
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);
} } }
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)
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.
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; } }
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 {