Documente Academic
Documente Profesional
Documente Cultură
C1 - 2011 Arhitectura NET
C1 - 2011 Arhitectura NET
ro
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.
Bibliografie 1. 2. 3. 4. Tom Archer : Inside C# Second Edition Tom Barnaby : Distributed .NET Programming in C# Joseph C. Rattz, Jr. : Language Integrated Query in C# 2008 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 Reflector, ILSpy FxCop ILDASM Microsoft (dezasamblor)
Curs 1 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. Mostenire. Polimorfism.
Componente principale pentru .NET: CLR Common Language Runtime; o CTS Common Type System; 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 de distribuire a aplicatiilor simplificat (nu mai e nevoie de inregistrare in registri, se permit multiple versiuni ale aceleasi biblioteci *.dll) . etc.
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.
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.
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.
Spatiul de nume (namespace) Namespace constituie o grupare logica de tipuri, continute intr-un assembly. Exemplu
System.IO contine tipuri legate de 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 assembly 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() 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.
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. Observatie Toate tipurile create sunt derivate din Object (direct sau indirect).
Echivalente
int <=> System.Int32 int n <=> System.Int32 n ;
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.
// 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 metoda Main (ce constituie entry point pentru aplicatie) scriem urmatorul cod pentru testare:
class Program { static void Main(string[] args) { Info info = new Info(); info.PrintHostName();
// Tip valoare creat cu operatorul new InfoStructure infoStr = new InfoStructure(info, Environment.SystemDirectory); infoStr.PrintInfoStructure(); // sau InfoStructure creat fara a folosi operatorul new InfoStructure infos; infos.info = new Info(); infos.SystemDirectory = Environment.SystemDirectory; infos.an = 2013; infos.PrintInfoStructure(); Console.ReadLine(); } }
Tipuri referinta : 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. 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.
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public Object(); // Summary: // Determines whether the specified System.Object is equal to // the current System.Object. // // Parameters: // obj: // The System.Object to compare with the current System.Object. // // Returns: // true if the specified System.Object is equal to the current // System.Object; // otherwise, false. // // Exceptions: // System.NullReferenceException: // The obj parameter is null. public virtual bool Equals(object obj); // // Summary: // Determines whether the specified System.Object instances are // considered equal. // // Parameters: // objA: // The first System.Object to compare. // // objB: // The second System.Object to compare. public static bool Equals(object objA, object objB); // // Summary: // Serves as a hash function for a particular type. // // Returns: // A hash code for the current System.Object. public virtual int GetHashCode(); // // Summary: // Gets the System.Type of the current instance. // // Returns: // The System.Type instance that represents the exact runtime // type of the current instance. public Type GetType(); // // Summary: // Creates a shallow copy of the current System.Object. // // Returns: // A shallow copy of the current System.Object. protected object MemberwiseClone(); // // Summary:
// Determines whether the specified System.Object instances are // the same instance. // // Parameters: // objA: // The first System.Object to compare. // // objB: // The second System.Object to compare. // // Returns: // true if objA is the same instance as objB or if both are // null references; // otherwise, false. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static bool ReferenceEquals(object objA, object objB); // // Summary: // Returns a System.String that represents the current // System.Object. // // Returns: // A System.String that represents the current System.Object. public virtual string ToString(); } }
Instantierea clasei (crearea obiectului) se face folosind operatorul new. Operatorul new poate fi folosit si pentru tipul valoare. Crearea obiectului poate fi facuta astfel:
Persoana p = new Persoana(X, 12);
sau
Persoana p; // nu se creaza obiectul. p are valoarea null. 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 ce initializeaza aceeasi variabila de doua ori (FxCop semnaleaza acest lucru)
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. 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 public protected private Descriere Membrul este accesibil din afara definitiei clasei si a ierarhiei claselor derivate. Membrul nu este vizibil in afara clasei si poate fi accesat numai de clasele derivate. Membrul nu este vizibil in afara clasei si nici in clasele derivate. 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. Accesul este limitat la assembly-ul curent sau la tipurile derivate din clasa continuta.
internal
protected internal *
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 class sealed ClasaFinala { ... } } namespace InfoNested { // este internal class Persoana { // clasa imbricata (nested class in engleza) private class Copil { ... } } 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 Permite modificarea accesibilitatii implicita enum public private public class protected (aici class internal apare ca data membru in alta private clasa) protected internal interface public struct private public internal private
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 ? Raspuns: !!! Raspunsul il gasim in urmatorul cod:
public class InfoStatic {
// tip referinta clasa Info creata anterior in acest curs 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); } }
Ctor 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. Ex.
class Bicicleta { public string model; public static int nRoti; // nu are modificatori de acces si nici parametri static Bicicleta () { nRoti = 2; } public Bicicleta() { model = necunoscut ; } }
namespace ExtensionMethod { /// <summary> /// Metode extinse pentru tipul System.Int32 /// Clasa trebuie sa fie statica /// Metoda trebuie sa fie statica /// 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. // Atentie la cati parametri se furnizeaza in // momentul apelului. 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); // 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 appel fara parametri int ns = n.InversareIntreg(); Console.WriteLine("Intreg initial = {0}, si inversat = {1}",n, ns); // Apel metoda extinsa ce aduna doua numere intregi // Appel cu un parametru. 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.
Metode
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 instantei 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. 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 ?
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.
Tipul enumerare din CTS - 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 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) ;
Delegate 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, constant, event}.
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 System.SByte System.Int16 System.Int32 System.Int64 System.UInt16 System.UInt32 System.UInt64 System.Single System.Double System.Object System.Char System.String System.Decimal System.Boolean
Byte SByte Short Integer Long UShort UInteger ULong Single Double Object Char String Decimal Boolean
byte sbyte short int long ushort uint ulong float double object char string decimal bool
unsigned char signed char short int or long __int64 unsigned short unsigned int or unsigned long unsigned __int64 Float Double Object^ wchar_t String^ Decimal Bool
Exemplu complet de definire a unui tip. Veti face acest exemplu la laborator.
using using using using System; System.Collections.Generic; System.Linq; System.Text;
namespace C1_2012 { /// <summary> /// Exemplu de definre a unei clase /// </summary> public class Info { // ctor public Info() { } // Date membru private. Vor fi accesate prin proprietati. private int numar; private string nume; // folosit pentru a defini indexer int[] tablouInt = new int[20]; // Date membru publice /// <summary> /// Descriere pentru data membru x. Acest text va fi afisat /// de Intellisense. /// </summary> public float x; // Date membru statice public static decimal Media; // Proprietati public int Numar { get { return numar; } set { numar = value; } } public string Nume { get { return nume; } set { if (((string)value).Length >= 3) nume = value; } }
// Proprietati automate sunt numai R/W public string ComputerName { get; set; } public int An { get; set; } // indexer public int this[int i] { get { if (i < 20 && i >= 0) return tablouInt[i]; else throw new IndexOutOfRangeException(); } set { if (i < 20 && i >= 0) tablouInt[i] = value; else throw new IndexOutOfRangeException(); } } // Event // EventHandler este un delagate pentru evenimente ce nu au date. public event EventHandler EventInfo; // Metoda ce lanseaza evenimentul public void OnInfo() { if (EventInfo != null) EventInfo(this, null); } // Event cu delegate propriu public delegate void Feedback(object source, int n); public event Feedback EventFeedback; public void OnEventFeedback(object source, int n) { if (EventFeedback != null) EventFeedback(source, n); } // Metoda a instantei public int CalculInt(int x) { OnInfo(); return x + 1; } // Metoda statica /// <summary> /// Acest text va fi afisat de Intellisense /// </summary> /// <param name="d"></param> /// <returns></returns> public static double CalculDouble(double d)
{ return d / 2.0; } // Supraincarcare operatori // Operatorul == se implementeaza impreuna cu operatorul != public static bool operator ==(Info i1, Info i2) { if (!i1.Equals(i2)) return false; return true; } public static bool operator !=(Info i1, Info i2) { return !(i1 == i2); } } }
namespace C1_2012 { class Program { static void Main(string[] args) { // creare obiect Info info = new Info(); // atasare metoda pentru evenimentul EventInfo info.EventInfo += new EventHandler(info_EventInfo); // apel metoda a instantei info.OnInfo(); int x = info.CalculInt(2); // folosire prorpietati info.An = 2012; // set info.ComputerName = Environment.MachineName; //set int an = info.An; // get // Folosire indexer info[0] = 2; Console.WriteLine("Indexer {0}", info[0]); // apel metoda statica double d = Info.CalculDouble(20.0); // Test operator == si != TestOperatorEgalDiferit(); }
// Metoda pentru event EventInfo static void info_EventInfo(object sender, EventArgs e) { Console.WriteLine("EventInfo ... object = {0}", sender.ToString()); //throw new NotImplementedException(); } private static void TestOperatorEgalDiferit() { Console.WriteLine("\nTest operator == \n"); Info i1 = new Info(); Info i2 = new Info(); Info i11 = i1; if (i1 == i2) Console.WriteLine("i1 si i2 sunt aceleasi obiecte"); else Console.WriteLine("i1 diferit de i2"); if (i1 == i11) Console.WriteLine("i1 si i11 sunt aceleasi obiecte"); else Console.WriteLine("i1 diferit de i11"); Console.WriteLine("\nTest operator !=\n"); if (i1 != i2) Console.WriteLine("i1 diferit de i2"); else Console.WriteLine("i1 si i2 sunt aceleasi obiecte"); // i1 si i11 sunt aceleasi obiecte if (i1 != i11) Console.WriteLine("i1 si i11 nu sunt aceleasi obiecte"); else Console.WriteLine("i1 si i11 sunt aceleasi obiecte..."); } } }
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(); 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?
2. Pasarea unui tip referinta prin referinta ref / out vezi ex C1_2009 din d:\exenet
using using using using System; System.Collections.Generic; System.Linq; 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); } } // 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); } } }
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 Unde este tipul alocat? Cum este reprezentata variabila? Care este tipul de baza ? Poate acest tip sa fie baza pentru alte tipuri ? Care e tipul implicit pentru pasarea parametrilor? Tip valoare Pe stiva Copii locale
System.ValueType
Tip referinta In managed heap. Puncteaza la memoria alocata instantei Derivat din orice ce nu e ValueType sau sealed. Da, daca nu e sealed. Referinta.
Poate acest tip sa suprascrie (override) metoda System.Object.Finalize()? Pot defini ctori pt acest tip ?
Nu. Tipurile valoare nu fac obiectul procesului de garbage collection, sunt alocate pe stiva. Da. Numai ctor cu parametru, ctor implicit este rezervat. Cand sunt in afara blocului unde au fost definite.
DA!
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;
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( 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); } }
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; } } 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); } }
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.
In CIL avem:
.method private hidebysig static void managed Main(string[] args) cil
{ .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 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 mostenirii 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 privat. 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 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(); } }
{ protected Persoana[] persoane; public void LoadPersoane() { 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:
?
Clasa derivata:
class Circle : Shape { public Circle() {} 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); } }
} 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
Proprietati
Proprietatile nu permit accesul direct la membri. Sunt furnizati doi accesori (set, get) ce lucreaza direct cu data membru. Scopul este de a pastra date cit mai corecte in obiectul instantiat. 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. modificatorul static poate fi utilizat pentru proprietate. Modificatorii de acces trebuiesc specificati la nivel de proprietate. Proprietati automate : 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.
Suprascrierea proprietatilor mostenite Trebuie specificata proprietatea 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; } 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"; } } }
In exemplul urmator este descrisa suprascrierea proprietatilor mostenite (proprietatile sunt declarate abstract).
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; } }
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; } } }
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. Mostenire. :base Polimorfism.
Probleme propuse: Observatie: Problemele se considera complet rezolvate daca se prezinta si codul ce testeaza implementarile.
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( ); }