Clase și obiecte în C#

Tradus din această pagină oficială de documentație Microsoft.

Clasele sunt cele mai fundamentale dintre tipurile C#. O clasă este o structură de date care combină stare (câmpuri) și acțiuni (metode și alte membri funcții) într-o singură unitate. O clasă furnizează o definiție pentru instanțe create dinamic ale clasei, cunoscute și ca obiecte. Clasele suportă moștenire și polimorfism, mecanisme prin care clase derivate pot extinde și specializa clase bază.

Clase noi sunt create folosind declarații de clase. O declarație de clasă începe cu un antet care specifică atributele și modificatorii clasei, numele clasei, clasa bază (dacă este dată), și interfețele implementate de clasă. Antetul este urmat de corpul clasei, care consistă dintr-o listă de declarații de membri scrise între delimitatorii { și }.

Următoarea este o declarație a unei clase simple numită Point (ro. Punct):

public class Point
{
    public int x, y;
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}


Instanțe de clase sunt create folosind operatorul new, care alocă memorie pentru o nouă instanță, invocă un constructor să inițializeze instanța, și întoarce on referință către instanță. Următoarele instrucțiuni creează două obiecte Point și rețin referințe la aceste obiecte în două variabile:

Point p1 = new Point(0, 0);
Point p2 = new Point(10, 20);


Memoria ocupată de un obiect este automat revendicată când obiectul nu mai este accesibil. Nu este nici necesar și nici posibil să se dealoce obiecte explicit în C#.

Membri

Membrii unei clase sunt fie membri statici fie membri de instanță. Membrii statici aparțin claselor, și membrii de instanță aparțin obiectelor (instanțe ale claselor).

Cele ce urmează oferă o vedere de ansamblu a felurilor de membri pe care o clasă îi poate conține.
  • Constante
    • Valori constante asociate cu clasa
  • Câmpuri
    • Variabile ale clasei
  • Metode
    • Calcule și acțiuni care pot fi realizate de clasă
  • Proprietăți
    • Acțiuni asociate cu scrierea și citirea de proprietăți numite ale clasei
  • Indexatori
    • Acțiuni asociate cu indexarea instanțelor clasei ca un tablou
  • Evenimente
    • Notificări care pot fi generate de clasă
  • Operatori
    • Operatori de conversii și de expresii suportați de clasă
  • Constructori
    • Acțiuni necesare pentru inițializarea instanțelor clasei sau clasei în sine
  • Finalizatori
    • Acțiuni de realizat înainte ca instanțele clasei să fie permanent abandonate
  • Tipuri
    • Tipuri îmbricate declarate de clasă

Accesibilitate

Fiecare membru al unei clase are o accesibilitate asociată, care controlează regiunile textului programului care pot accesa membrul. Există șase forme posibile de accesibilitate. Acestea sunt sumarizate dedesubt.
  • public
    • Accesul nu este limitat
  • protected
    • Accesul este limitat la această clasă sau clase derivate din această clasă
  • internal
    • Accesul este limitat la ansamblul curent (.exe, .dll, etc.)
  • protected internal
    • Accesul este limitat la clasa conținătoare, clase derivate din clasa conținătoare, sau clase din același ansamblu
  • private
    • Accesul este limitat la clasa aceasta
  • private protected
    • Accesul este limitat la clasa conținătoare sau clase derivate din tipul conținător din același ansamblu

Parametri de tip

O definiție a unei clase poate specifica o mulțime de parametri de tip prin urmarea numelui clasei cu paranteze unghiulare încadrând o listă de nume de parametri de tip. Parametrii de tip pot fi apoi folosiți în corpul declarațiilor clasei pentru a defini membrii clasei. In următorul exemplu, parametrii de tip ai Pair sunt TFirst și TSecond:

public class Pair<TFirst,TSecond>
{
    public TFirst First;
    public TSecond Second;
}

Un tip clasă care este declarat să primească parametri de tip este numit un tip de clasă generic. Tipurile struct, interface și delegate pot de asemenea fi generice. Când clasa generică este folosită, argumentele de tip trebuie să fie furnizate pentru fiecare din parametrii de tip.

Pair<int,string> pair = new Pair<int,string> { First = 1, Second = "doi" };
int i = pair.First;     // TFirst este int
string s = pair.Second; // TSecond este string

Un tip generic cu argumente de tip furnizate, precum Pair<int,string> mai sus, este numit un tip construit.

Clase bază

O declarație de clasă poate specifica o clasă bază urmând numele clasei și parametri de tip cu două puncte și numele clasei bază. Omițând specificarea unei clase bază este același lucru cu derivarea din tipul object. In următorul exemplu, clasa bază a Point3D este Point, și clasa bază a Point este object:

public class Point
{
    public int x, y;
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}
public class Point3D: Point
{
    public int z;
    public Point3D(int x, int y, int z) :
        base(x, y)
    {
        this.z = z;
    }
}


O clasă moștenește membrii clasei sale bază. Moștenirea înseamnă că o clasă conține în mod implicit toți membrii clasei sale bază, cu excepția constructorilor de instanță și statici, și finalizatorii clasei bază. O clasă derivată poate adăuga noi membrii la aceia pe care îi moștenește, dar nu poate înlătura definiția unui membru moștenit. In exemplul precedent, Point3D moștenește câmpurile x și y de la Point, și fiecare instanță Point3D conține trei câmpuri, x, y, și z.

O conversie implicită există de la un tip clasă la oricare din tipurile claselor sale bază. Prin urmare, o variabilă de un tip clasă poate referi o instanță a acelei clase sau o instanță a oricărei clase derivate. De exemplu, date fiind declarațiile anterioare de clase, o variabilă de tip Point poate referi fie un Point fie un Point3D:

Point a = new Point(10, 20);
Point b = new Point3D(10, 20, 30);

Câmpuri

Un câmp este o variabilă care este asociată cu o clasă sau cu o instanță a unei clase.

Un câmp declarat cu modificatorul static definește un câmp static. Un câmp static identifică exact o locație de stocare. Indiferent de câte instanțe ale unei clase sunt create, există întotdeauna doar o copie a câmpului static.

Un câmp declarat fără modificatorul static definește un câmp de instanță. Fiecare instanță a unei clase conține o copie separată a tuturor câmpurilor de instanță ale acelei clase.

In următorul exemplu, fiecare instanță a clasei Color are o copie separată a câmpurilor de instanță r, g, și b, dar există doar o singură copie a câmpurilor statice Black, White, Red, Green, și Blue:

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue = new Color(0, 0, 255);
    private byte r, g, b;
    public Color(byte r, byte g, byte b)
    {
        this.r = r;
        this.g = g;
        this.b = b;
    }
}


Cum s-a arătat în exemplul anterior, câmpuri doar-pentru-citire pot fi declarate cu modificatorul readonly. Atribuirea la un câmp readonly poate avea loc doar ca parte a declarației câmpului sau într-un constructor în aceeași clasă.

Metode

O metodă este un membru care implementează un calcul sau o acțiune care pot fi realizate de un obiect sau de o clasă. Metodele statice sunt accesate prin clasă. Metodele de instanță sunt accesate prin instanțe ale clasei.

Metodele pot avea o listă de parametri, care reprezintă valori sau referințe de variabile transmise metodei, și un tip de întoarcere, care specifică tipul valorii calculate și întoarse de metodă. Tipul de întoarcere al unei metode este void dacă nu întoarce o valoare.

Precum tipurile, metodele pot de asemenea să aibă o mulțime de parametri de tip, pentru care argumente de tip trebuie să fie specificate când metoda este apelată. Spre deosebire de tipuri, argumentele de tip pot deseori fi deduse din argumentele unui apel de metodă și nu trebuie date explicit.

Semnătura unei metode trebuie să fie unică în clasa în care metoda este declarată. Semnătura unei metode consistă în numele metodei, numărul de parametri de tip și în numărul, modificatorii, și tipurile parametrilor ei. Semnătura unei metode nu include tipul de întoarcere.

Parametri

Parametrii sunt folosiți pentru a transmite valori sau referințe de variabile la metode. Parametrii unei metode își iau valorile reale din argumentele care sunt specificate când metoda este invocată. Există patru feluri de parametri: parametri valoare, parametri referință, parametri de ieșire, și tablouri de parametri.

Un parametru valoare este folosit pentru transmiterea argumentelor de intrare. Un parametru valoare corespunde unei variabile locale care își ia valoarea inițială de la argumentul care a fost transmis pentru parametru. Modificările pentru un parametru valoare nu afectează argumentul care a fost transmis pentru parametru.

Parametrii valoare pot fi opționali, specificând o valoare implicită astfel încât argumentele corespunzătoare pot fi omise.

Un parametru referință este folosit pentru transmiterea argumentelor prin referință. Argumentul transmis pentru un parametru referință trebuie să fie o variabilă cu o valoare definită, și în timpul execuției metodei, parametrul referință reprezintă aceeași locație de stocare ca variabila argument. Un parametru referință este declarat cu modificatorul ref. Următorul exemplu arată folosirea de parametri ref.

using System;
class RefExample
{
    static void Swap(ref int x, ref int y)
    {
        int temp = x;
        x = y;
        y = temp;
    }
    public static void SwapExample()
    {
        int i = 1, j = 2;
        Swap(ref i, ref j);
        Console.WriteLine($"{i} {j}");    // Scrie "2 1"
    }
}


Un parametru de ieșire este folosit pentru transmiterea argumentelor prin referință. Este similar cu un parametru referință, cu excepția că nu cere să îi fie atribuită o valoare în mod explicit la argumentul furnizat de apelant. Un parametru de ieșire este declarat cu modificatorul out. Următorul exemplu arată folosirea de parametri out folosind sintaxa introdusă în C# 7.

using System;
class OutExample
{
    static void Divide(int x, int y, out int result, out int remainder)
    {
        result = x / y;
        remainder = x % y;
    }
    public static void OutUsage()
    {
        Divide(10, 3, out int res, out int rem);
        Console.WriteLine("{0} {1}", res, rem); // Scrie "3 1"
    }
}


Un tablou de parametri permite un număr variabil de argumente să fie transmise la o metodă. Un tablou de parametri este declarat cu modificatorul params. doar ultimul parametru al unei metode poate fi un tablou de parametri, și tipul unui tablou de parametri trebuie să fie un tip de tablou uni-dimensional. Metodele Write și WriteLine ale clasei System.Console sunt exemple bune de folosire de tablouri de parametri. Ele sunt declarate după cum urmează.

public class Console
{
    public static void Write(string fmt, params object[] args) { }
    public static void WriteLine(string fmt, params object[] args) { }
    // ...
}


Intr-o metodă care folosește un tablou de parametri, tabloul de parametri se comportă exact ca un parametru obișnuit de un tip tablou. Totuși, într-o invocare a unei metode cu un tablou de parametri, este posibil să transmiteți fie un singur argument de tipul tabloului de parametri sau orice număr de argumente de tipul elementelor tabloului de parametri. In cazul din urmă, o instanță de tablou este automat creată și inițializată cu argumentele date. Acest exemplu:

Console.WriteLine("x={0} y={1} z={2}", x, y, z);

este echivalent cu scrierea următoarelor.

string s = "x={0} y={1} z={2}";
object[] args = new object[3];
args[0] = x;
args[1] = y;
args[2] = z;
Console.WriteLine(s, args);

Corpul metodei și variabile locale

Corpul unei metode specifică instrucțiunile care să se execute când metoda este invocată.

Corpul unei metode poate declara variabile care sunt specifice invocării metodei. Aceste variabile se cheamă variabile locale. O declarație de variabilă locală specifică un nume de tip, un nume de variabilă, și posibil o valoare inițială. Următorul exemplu declară variabila locală i cu o valoare inițială de zero și o variabilă locală j fără valoare inițială.

using System;
class Squares
{
    public static void WriteSquares()
    {
        int i = 0;
        int j;
        while (i < 10)
        {
            j = i * i;
            Console.WriteLine($"{i} x {i} = {j}");
            i = i + 1;
        }
    }
}


C# cere unei variabile locale să fie în mod sigur atribuită înainte ca valoarea ei să poată fi obținută. De exemplu, dacă declarația anteriorului i nu includea o valoare inițială, compilatorul ar fi raportat o eroare pentru ulterioarele utilizări ale lui i deoarece i nu ar fi fost în mod sigur atribuit în acele puncte din program.

O metodă poate folosi instrucțiuni return pentru a întoarce controlul la apelantul ei. Intr-o metodă care întoarce void, instrucțiunile return nu pot specifica o expresie. Intr-o metodă care întoarce non-void, instrucțiunile return trebuie să includă o expresie care calculează valoarea de întoarcere.

Metode statice și de instanță

O metodă declarată cu modificatorul static este o metodă statică. O metodă statică nu operează pe o instanță specifică și poate accesa direct doar membri statici.

O metodă declarată fără un modificator static este o metodă de instanță. O metodă de instanță operează pe o instanță specifică și poate accesa și membri statici și cei de instanță. Instanța pe care o metodă instanță a fost invocata poate fi explicit accesată ca this. Este o eroare să se facă referire la this într-o metodă statică.

Următoarea clasă Entity are și membri statici, și membri de instanță.

class Entity
{
    static int nextSerialNo;
    int serialNo;
    public Entity()
    {
        serialNo = nextSerialNo++;
    }
    public int GetSerialNo()
    {
        return serialNo;
    }
    public static int GetNextSerialNo()
    {
        return nextSerialNo;
    }
    public static void SetNextSerialNo(int value)
    {
        nextSerialNo = value;
    }
}


Fiecare instanță Entity conține un număr de serie (și probabil câteva alte informații care nu sunt arătate aici). Constructorul Entity (care este ca o metodă de instanță) inițializează noua instanță cu următorul număr de serie disponibil. Deoarece constructorul este un membru de instanță, îi este permis să acceseze și câmpul de instanță serialNo și câmpul static nextSerialNo.

Metodele statice GetNextSerialNo și SetNextSerialNo pot accesa câmpul static nextSerialNo, dar ar fi o eroare pentru ei să acceseze direct câmpul de instanță serialNo.

Următorul exemplu arată utilizarea clasei Entity.

using System;
class EntityExample
{
    public static void Usage()
    {
        Entity.SetNextSerialNo(1000);
        Entity e1 = new Entity();
        Entity e2 = new Entity();
        Console.WriteLine(e1.GetSerialNo());            // Scrie "1000"
        Console.WriteLine(e2.GetSerialNo());            // Scrie "1001"
        Console.WriteLine(Entity.GetNextSerialNo());    // Scrie "1002"
    }
}


Notați că metodele statice SetNextSerialNo și GetNextSerialNo sunt invocate pe clasă în timp ce metoda de instanță GetSerialNo este invocată pe instanțe ale clasei.

Metode virtual, override și abstract

Când o declarație de metodă de instanță include modificatorul virtual, metoda se spune că este o metodă virtuală. Când nici un modificator virtual nu este prezent, metoda se spune că este o metodă nonvirtuală.

Când o metodă virtuală este invocată, tipul din timpul execuției al instanței pentru care acea invocare are loc determină reala implementare de invocat a metodei. Intr-o invocare de metodă nonvirtuală, tipul din momentul compilării al instanței este factorul determinant.

O metodă virtuală poate fi suprascrisă într-o clasă derivată. Când o declarație de metodă de instanță include un modificator override, metoda suprascrie o metodă virtuală cu aceeași semnătură. In timp ce o declarație de metodă virtuală introduce o nouă metodă, o declarație de metodă override specializează o metodă virtuală moștenită existentă furnizând o nouă impementare a acelei metode.

O metodă abstractă este o metodă virtuală fără implementare. O metodă abstractă este declarată cu modificatorul abstract și este permisă doar într-o clasă care este de asemenea declarată abstract. O metodă abstractă trebuie să fie suprascrisă în fiecare clasă derivată non-abstractă.

Următorul exemplu declară o clasă abstractă, Expression, care reprezintă un nod de arbore expresie, și trei clase derivate, Constant, VariableReference, și Operation, care implementează noduri de arbore expresie pentru constante, referințe de variabile, și operații aritmetice. (Acesta este similar, dar nu trebuie confundat cu tipurile arbore expresie).

using System;
using System.Collections.Generic;
public abstract class Expression
{
    public abstract double Evaluate(Dictionary<string,object> vars);
}
public class Constant: Expression
{
    double value;
    public Constant(double value)
    {
        this.value = value;
    }
    public override double Evaluate(Dictionary<string,object> vars)
    {
        return value;
    }
}
public class VariableReference: Expression
{
    string name;
    public VariableReference(string name)
    {
        this.name = name;
    }
    public override double Evaluate(Dictionary<string,object> vars)
    {
        object value = vars[name];
        if (value == null)
        {
            throw new Exception("Unknown variable: " + name);
        }
        return Convert.ToDouble(value);
    }
}
public class Operation: Expression
{
    Expression left;
    char op;
    Expression right;
    public Operation(Expression left, char op, Expression right)
    {
        this.left = left;
        this.op = op;
        this.right = right;
    }
    public override double Evaluate(Dictionary<string,object> vars)
    {
        double x = left.Evaluate(vars);
        double y = right.Evaluate(vars);
        switch (op) {
            case '+': return x + y;
            case '-': return x - y;
            case '*': return x * y;
            case '/': return x / y;
        }
        throw new Exception("Unknown operator");
    }
}


Cele patru clase precedente pot fi folosite pentru a modela expresii aritmetice. De exemplu, folosind instanțe ale acestor clase, expresia x + 3 poate fi reprezentată după cum urmează.

Expression e = new Operation(
    new VariableReference("x"),
    '+',
    new Constant(3));


Metoda Evaluate a unei instanțe Expression este invocată pentru a evalua expresia dată și a produce o valoare double. Metoda primește un argument Dictionary care conține nume de variabile (ca chei ale înregistrărilor) și valori (ca valori ale înregistrărilor). Deoarece Evaluate este o metodă abstractă, clasele non-abstracte derivate din Expression trebuie să suprascrie Evaluate.

Implementarea unei Constant a Evaluate pur și simplu întoarce constanta stocată. Implementarea unei VariableReference caută numele variabilei în dicționar si întoarce valoarea rezultată. Implementarea unei Operation mai întâi evaluează operanzii stâng și drept (invocând recursiv metodele lor Evaluate) și apoi realizează operația aritmetică dată.

Următorul program folosește clasele Expression pentru a evalua expresia x * (y + 2) pentru valori diferite ale x și y.

using System;
using System.Collections.Generic;
class InheritanceExample
{
    public static void ExampleUsage()
    {
        Expression e = new Operation(
            new VariableReference("x"),
            '*',
            new Operation(
                new VariableReference("y"),
                '+',
                new Constant(2)
            )
        );
        Dictionary<string,object> vars = new Dictionary<string, object>();
        vars["x"] = 3;
        vars["y"] = 5;
        Console.WriteLine(e.Evaluate(vars));  // Scrie "21"
        vars["x"] = 1.5;
        vars["y"] = 9;
        Console.WriteLine(e.Evaluate(vars));  // Scrie "16.5"
    }
}

Supraîncărcarea metodelor

Supraîncărcarea metodelor permite mai multor metode din aceeași clasă să aibă același nume cât timp ele au semnături unice. Când se compilează o invocare a unei metode supraîncărcate rezoluția de supraîncărcare pentru a determina metoda specifică de invocat. Rezoluția de supraîncărcare găsește singura metodă care se potrivește cel mai bine cu argumentele și raportează o eroare dacă nici o singură potrivire bună nu poate fi găsită. Următorul exemplu arată rezoluția de supraîncărcare în efect. Comentariul pentru fiecare invocare în metoda UsageExample arată care metodă este de fapt invocată.

using System;
class OverloadingExample
{
    static void F()
    {
        Console.WriteLine("F()");
    }
    static void F(object x)
    {
        Console.WriteLine("F(object)");
    }
    static void F(int x)
    {
        Console.WriteLine("F(int)");
    }
    static void F(double x)
    {
        Console.WriteLine("F(double)");
    }
    static void F<T>(T x)
    {
        Console.WriteLine("F<T>(T)");
    }
    static void F(double x, double y)
    {
        Console.WriteLine("F(double, double)");
    }
    public static void UsageExample()
    {
        F();            // Invoca F()
        F(1);           // Invoca F(int)
        F(1.0);         // Invoca F(double)
        F("abc");       // Invoca F<string>(string)
        F((double)1);   // Invoca F(double)
        F((object)1);   // Invoca F(object)
        F<int>(1);      // Invoca F<int>(int)
        F(1, 1);        // Invoca F(double, double)
    }
}


După cum a arătat exemplul, o metodă particulară întotdeauna poate fi selectată convertind explicit argumentele la tipurile exacte ale parametrilor și/sau furnizând argumentele de tip în mod explicit.

Alți membri funcții

Membri care conțin cod executabil sunt cunoscuți în mod comun ca membri funcții ai unei clase. Secțiunea precedentă descrie metode, care sunt felul principal de membri funcții. Această secțiune descrie celelalte feluri de membri funcții suportate de C#: constructori, proprietăți, indexatori, evenimente, operatori, și finalizatori.

Cele ce urmează arată o clasă generică numită List, care implementează o listă dezvoltabilă de obiecte. Clasa conține câteva exemple ale celor mai comune feluri de membri funcții.

public class List<T>
{
    // Constanta
    const int defaultCapacity = 4;


    // Campuri
    T[] items;
    int count;


    // Constructor
    public List(int capacity = defaultCapacity)
    {
        items = new T[capacity];
    }


    // Proprietati
    public int Count => count; 


    public int Capacity
    {
        get { return items.Length; }
        set
        {
            if (value < count) value = count;
            if (value != items.Length)
            {
                T[] newItems = new T[value];
                Array.Copy(items, 0, newItems, 0, count);
                items = newItems;
            }
        }
    }


    // Indexator
    public T this[int index]
    {
        get
        {
            return items[index];
        }
        set
        {
            items[index] = value;
            OnChanged();
        }
    }
   
    // Metode
    public void Add(T item)
    {
        if (count == Capacity) Capacity = count * 2;
        items[count] = item;
        count++;
        OnChanged();
    }
    protected virtual void OnChanged() =>
        Changed?.Invoke(this, EventArgs.Empty);


    public override bool Equals(object other) =>
        Equals(this, other as List<T>);


    static bool Equals(List<T> a, List<T> b)
    {
        if (Object.ReferenceEquals(a, null)) return Object.ReferenceEquals(b, null);
        if (Object.ReferenceEquals(b, null) || a.count != b.count)
            return false;
        for (int i = 0; i < a.count; i++)
        {
            if (!object.Equals(a.items[i], b.items[i]))
            {
                return false;
            }
        }
        return true;
    }


    // Eveniment
    public event EventHandler Changed;


    // Operatori
    public static bool operator ==(List<T> a, List<T> b) =>
        Equals(a, b);


    public static bool operator !=(List<T> a, List<T> b) =>
        !Equals(a, b);
}

Constructori

C# suportă și constructori de instanță și constructori statici. Un constructor de instanță este un membru care implementează acțiunile necesare pentru a inițializa o instanță a unei clase. Un constructor static este un membru care implementează acțiunile necesare pentru a inițializa clasa în sine când este prima dată încărcată.

Un constructor este declarat ca o metodă fără tip de întoarcere și cu același nume ca clasa conținătoare. Dacă declarația unui constructor include un modificator static, ea declară un constructor static. Altfel, ea declară un constructor de instanță.

Constructorii de instanță pot fi supraîncărcați, și pot avea parametri opționali. De exemplu, clasa List<T> declară doi constructori de instanță, unul fără parametri și unul care primește un parametru int. Constructorii de instanță sunt invocați folosind operatorul new. Următoarele instrucțiuni alocă două instanțe List<string> folosind constructorul clasei List cu și fără argumentul opțional.

List<string> list1 = new List<string>();
List<string> list2 = new List<string>(10);


Spre deosebire de alți membri, constructorii de instanță nu se moștenesc, și o clasă nu are nici un alt constructor de instanță decât cei declarați de fapt în clasă. Dacă nici un constructor de instanță nu este furnizat pentru o clasă, atunci unul gol fără parametri este furnizat automat.

Proprietăți

Proprietățile sunt o extensie naturală a câmpurilor. Ambele sunt membri numiți cu tipuri asociate, și sintaxa pentru accesarea câmpurilor și proprietăților este aceeași. Totuși, spre deosebire de câmpuri, proprietățile nu denotă locații de stocare. In schimb, proprietățile au accesori care specifică instrucțiunile de executat când valorile lor sunt citite sau scrise.

O proprietate este declarată ca un câmp, exceptând faptul că declarația se termină cu un accesor get și/sau un accesor set scris între delimitatori { și } în loc de a se termina cu punct și virgulă. O proprietate care are și accesor get și accesor set este o proprietate citire-scriere, o proprietate care are doar un accesor get este o proprietate doar-pentru-citire, și o proprietate care are doar un accesor set este o proprietate doar-pentru-scriere.

Un accesor get corespunde cu o metodă fără parametri cu valoarea de întoarcere având tipul proprietății. Cu excepția când este ținta unei atribuiri, când o proprietate este referită într-o expresie, accesorul get al proprietății este invocat pentru a calcula valoarea proprietății.

Un accesor set corespunde unei metode cu un singur parametru numit value și nici un tip de întoarcere. Când o proprietate este referită ca ținta unei atribuiri sau ca operandul lui ++ sau --, accesorul set este invocat cu un argument care furnizează noua valoare.

Clasa List<T> declară două proprietăți, Count și Capacity, care sunt doar-pentru-citire și, respectiv, citire-scriere. In cele ce urmează, este un exemplu de utilizare a acestor proprietăți.

List<string> names = new List<string>();
names.Capacity = 100;   // Invoca accesorul set
int i = names.Count;    // Invoca accesorul get
int j = names.Capacity; // Invoca accesorul get


Similar cu câmpurile și metodele, C# suportă și proprietăți de instanță și proprietăți statice. Proprietățile statice sunt declarate cu modificatorul static, și proprietățile de instanță sunt declarate fără el.

Accesorul/ii unei proprietăți pot fi virtuali. Când o declarație de proprietate include un modificator virtual, abstract, sau override, el se aplică accesorului/ilor proprietății.

Indexatori

Un indexator este un membru care dă voie obiectelor să fie indexate în același fel ca un tablou. Un indexator este declarat ca o proprietate cu excepția că numele membrului este this urmat de o listă de parametri scrisă între delimitatorii [ și ]. Parametrii sunt disponibili în accesorul/ii indexatorului. Similar proprietăților, indexatorii pot fi citire-scriere, doar-pentru-citire, și doar-pentru-scriere, și accesorul/ii unui indexator pot fi virtuali.

Clasa List declară un singur indexator citire-scriere care primește un parametru int. Indexatorul face posibilă indexarea instanțelor List cu valori int. De exemplu:

List<string> names = new List<string>();
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
for (int i = 0; i < names.Count; i++)
{
    string s = names[i];
    names[i] = s.ToUpper();
}


Indexatorii pot fi supraîncărcați, aceasta însemnând că o clasă poate declara mai mulți indexatori cât tip numărul sau tipurile parametrilor lor diferă.

Evenimente

Un eveniment este un membru care dă voie unei clase sau unui obiect să ofere notificări. Un event este declarat ca un câmp cu excepția că declarația include un cuvânt cheie event și tipul trebuie să fie un tip delegat.

Intr-o clasă care declară un membru eveniment, evenimentul se comportă exact ca un câmp de un tip delegat (cu condiția că evenimentul nu este abstract și nu declară accesori). Câmpul stochează o referință la un delegat care reprezintă gestionarul de eveniment care a fost adăugat la eveniment. Când nici un gestionar de eveniment nu este prezent, câmpul este null.

Clasa List<T> declară declară un singur membru eveniment chemat Changed. care indică faptul că un nou element a fost adăugat la listă. Evenimentul Changed este ridicat de metoda virtuală OnChanged, care mai întâi verifică dacă evenimentul este null (însemnând că nici un gestionar nu este prezent). Noțiunea de ridicare a unui eveniment este precis echivalentă cu invocarea delegatului reprezentat de eveniment — în concluzie, nu există nici un construct special de limbaj pentru ridicarea evenimentelor.

Clienții reacționează la evenimente prin gestionare de evenimente. Gestionarele de evenimente sunt atașate folosind operatorul += și înlăturate folosind operatorul -=. Următorul exemplu atașează un gestionar de eveniment la evenimentul Changed al unui List<string>.

class EventExample
{
    static int changeCount;
    static void ListChanged(object sender, EventArgs e)
    {
        changeCount++;
    }
    public static void Usage()
    {
        List<string> names = new List<string>();
        names.Changed += new EventHandler(ListChanged);
        names.Add("Liz");
        names.Add("Martha");
        names.Add("Beth");
        Console.WriteLine(changeCount);  // Scrie "3"
    }
}


Pentru scenarii avansate în care controlul stocării care stă la baza unui eveniment este dorit, o declarație de eveniment poate furniza explicit accesori add și remove, care sunt întrucâtva similare cu accesorul set al unei proprietăți.

Operatori

Un operator este un membru care definește înțelesul aplicării unui operator de expresie particular pe instanțe ale unei clase. Trei tipuri de operatori pot fi definiți: operatori unari, operatori binari, și operatori de conversie. Toți operatorii trebuie să fie declarați ca public și static.

Clasa List<T> declară doi operatori, operator == și operator !=, și deci dă înțeles nou expresiilor care aplică aceste operații pe instanțe List. Specific, operatorii definesc egalitatea a două instanțe List<T> precum compararea fiecărui obiect conținut folosind metodele lor Equals. Următorul exemplu folosește operatorul == pentru a compara două instanțe List<int>.

List<int> a = new List<int>();
a.Add(1);
a.Add(2);
List<int> b = new List<int>();
b.Add(1);
b.Add(2);
Console.WriteLine(a == b);  // Scrie "True"
b.Add(3);
Console.WriteLine(a == b);  // Scrie "False"


Primul Console.WriteLine scrie True deoarece cele două liste conțin același număr de obiecte cu aceleași valori în aceeași ordine. Dacă List<T> nu definea operator ==, primul Console.WriteLine ar fi afișat False deoarece a și b referă instanțe List<int> diferite.

Finalizatori

Un finalizator este un membru care implementează acțiunile necesare pentru a finaliza o instanță a unei clase. Finalizatorii nu pot avea parametri , ei nu pot avea modificatori de accesibilitate, și ei nu pot fi invocați explicit. Finalizatorul pentru o instanță este invocat automat în timpul „colectării gunoiului”.

„Colectorulului de gunoi” îi este permisă libertate mare în deciderea când să colecteze obiectele și să ruleze finalizatorii. Specific, sincronizarea invocărilor finalizatorilor nu este deterministă, și finalizatorii pot fi executați pe oricare fir de execuție. Pentru acestea și alte motive, clasele ar trebui să implementeze finalizatori doar când nici o altă soluție nu este fezabilă.

Instrucțiunea using oferă o mai bună abordare a distrugerii obiectelor.

Tradus din această pagină oficială de documentație Microsoft.

Niciun comentariu:

Trimiteți un comentariu