Tablouri C#

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

Un tablou este o structură de date care conține un număr de variabile care sunt accesate prin indici calculați. Variabilele conținute într-un tablou, de asemenea numite elemente ale tabloului, sunt toate de același tip, și acest tip se cheamă tipul de elemente al tabloului.
Tipurile tablou sunt tipuri referință, și declarația unei variabile tablou pur și simplu pune de o parte spațiu pentru o referință către o instanță de tablou. Instanțele propriu zise de tablou sunt create dinamic la momentul execuției folosind operatorul new. Operația new specifică lungimea unei noi instanțe de tablou, care este apoi fixă pentru toată durata de viață a instanței. Indicii elementelor unui tablou sunt cuprinse între 0 și Length – 1. Operatorul new inițializează automat elementele unui tablou la valoarea lor implicită, care, de exemplu, este zero pentru toate tipurile numerice și null pentru toate tipurile referință.


Următorul exemplu creează un tablou de elemente int, inițializează tabloul, și afișează conținutul tabloului.

using System;
class ArrayExample
{
    static void Main()
    {
        int[] a = new int[10];
        for (int i = 0; i < a.Length; i++)
        {
            a[i] = i * i;
        }
        for (int i = 0; i < a.Length; i++)
        {
            Console.WriteLine($”a[{i}] = {a[i]}”);
        }
    }

}
Acest exemplu creează și operează pe un tablou uni-dimensional. C# suportă de asemenea tablouri multi-dimensionale. Numărul de dimensiuni ale unui tip tablou, cunoscut și ca rang-ul tipului tablou, este unu plus numărul de virgule scrise între parantezele drepte ale tipului tablou. Următorul exemplu alocă un tablou uni-dimensional, unul bi-dimensional, și, respectiv, unul tri-dimensional.
int[] a1 = new int[10];
int[,] a2 = new int[10, 5];
int[,,] a3 = new int[10, 5, 2];
Tabloul a1 conține 10 elemente, tabloul a2 conține 50 (10 × 5) elemente, și tabloul a3 conține 100 (10 × 5 × 2) elemente. Tipul de element al unui tablou poate fi oricare tip, inclusiv un tip tablou. Un tablou cu elemente de un tip tablou este uneori numit un tablou crestat (en. jagged array) deoarece lungimile tablourilor elemente nu trebuie să fie toate la fel. Următorul exemplu alocă un tablou de tablouri de int:

int[][] a = new int[3][];
a[0] = new int[10];
a[1] = new int[5];
a[2] = new int[20];

Prima linie creează un tablou cu trei elemente, fiecare de tip int[] și fiecare cu o valoare inițială null. Liniile ulterioare apoi inițializează cele trei elemente cu referințe la instanțe de tablouri individuale de lungimi variabile.

Operatorul new permite valorilor inițiale ale elementelor tabloului să fie specificate folosind un inițializator de tablou, care este o listă de expresii scrise între delimitatorii { și }. Următorul exemplu alocă și inițializează un int[] cu trei elemente.

int[] a = new int[] {1, 2, 3};

Notați că lungimea tabloului este dedusă din numărul de expresii dintre { și }. Variabilele locale și declarațiile de câmpuri pot fi scurtate mai departe astfel încât tipul tablou nu trebuie să fie reafirmat.

int[] a = {1, 2, 3};

Ambele exemple anterioare sunt echivalente cu următorul:

int[] t = new int[3];
t[0] = 1;
t[1] = 2;
t[2] = 3;
int[] a = t;

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

Struct-uri C#

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

La fel ca clasele, struct-urile sunt structuri de date care pot conține membrii date și membri funcții, dar spre deosebire de clase, struct-urile sunt tipuri valoare și nu cer alocare pe heap. O variabilă de un tip struct stochează direct datele struct-urii, în timp ce o variabilă de un tip clasă stochează o referință la un obiect alocat dinamic. Tipurile struct nu suportă moștenire specificată de utilizator, și toate tipurile struct moștenesc implicit de la tipul ValueType, care la rândul lui moștenește implicit de la object.

Struct-urile sunt deosebit de folositoare pentru structuri mici de date care au înțeles de valoare. Numere complexe, puncte într-un sistem de coordonate, sau perechi cheie-valoare într-un dicționar sunt toate exemple bune de struct-uri. Utilizarea struct-urilor mai degrabă decât a claselor pentru structuri mici de date poate face o diferență enormă în numărul de alocări de memorie pe care le realizează o aplicație. De exemplu, următorul program creează și inițializează un tablou de 100 de puncte. Cu Point implementat ca o clasă, 101 de obiecte separate sunt instanțiate — unul pentru tablou și unul pentru fiecare din cele 100 de elemente.
public class PointExample
{
    public static void Main()
    {
        Point[] points = new Point[100];
        for (int i = 0; i < 100; i++)
            points[i] = new Point(i, i);
    }
}


O alternativă este să facem Point un struct.

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


Acum, doar un obiect este instanțiat unul pentru tablou și instanțele Point sunt stocate liniar în tablou.

Constructorii struct sunt invocați cu operatorul new, similar cu un constructor de clasă. Dar, în schimbul alocării dinamice a unui obiect pe heap-ul gestionat (en. managed) și a returnării unei referințe către el, un constructor de struct pur și simplu întoarce valoarea struct-urii în sine (de obicei ca o locație temporară pe stivă), și această valoare este apoi copiată după cât este necesar.

Cu clase, este posibil pentru două variabile să se refere la același obiect și deci posibil pentru operațiile asupra unei variabile să afecteze obiectul referit de cealaltă variabilă. Cu struct-uri, variabilele au fiecare propria lor copie a datelor, și nu este posibil pentru operațiile asupra uneia să afecteze cealaltă. De exemplu, ieșirea produsă de următorul fragment de cod depinde dacă Point este o clasă sau o struct.

Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);

Dacă Point este o clasă, ieșirea este 20 deoarece a și b se referă la același obiect. Dacă Point este o struct, ieșirea este 10 deoarece atribuirea lui a la b creează o copie a valorii, și această copie este neafectată de atribuiri ulterioare la a.x.

Exemplul anterior evidențiază două din limitările struct-urilor. In primul rând, copierea unei întregi struct este de obicei mai puțin eficientă decât copierea unei referințe de obiect, deci atribuirea și transmiterea parametrilor valoare pot fi mai scumpe cu struct-urile decât cu tipurile referință. In al doilea rând, cu excepția parametrilor in, ref, și out, nu este posibil să se creeze referințe la struct-uri, ceea ce exclude utilizarea lor într-un număr de situații.

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

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
{
    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 pair = new Pair { 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 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 vars);
}
public class Constant: Expression
{
    double value;
    public Constant(double value)
    {
        this.value = value;
    }
    public override double Evaluate(Dictionary vars)
    {
        return value;
    }
}
public class VariableReference: Expression
{
    string name;
    public VariableReference(string name)
    {
        this.name = name;
    }
    public override double Evaluate(Dictionary 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 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 vars = new Dictionary();
        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 x)
    {
        Console.WriteLine(„F(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)
        F((double)1);   // Invoca F(double)
        F((object)1);   // Invoca F(object)
        F(1);      // Invoca F(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
{
    // 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);


    static bool Equals(List a, List 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 a, List b) =>
        Equals(a, b);


    public static bool operator !=(List a, List 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 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 folosind constructorul clasei List cu și fără argumentul opțional.

List list1 = new List();
List list2 = new List(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 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 names = new List();
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 names = new List();
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 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.

class EventExample
{
    static int changeCount;
    static void ListChanged(object sender, EventArgs e)
    {
        changeCount++;
    }
    public static void Usage()
    {
        List names = new List();
        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 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 precum compararea fiecărui obiect conținut folosind metodele lor Equals. Următorul exemplu folosește operatorul == pentru a compara două instanțe List.

List a = new List();
a.Add(1);
a.Add(2);
List b = new List();
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 nu definea operator ==, primul Console.WriteLine ar fi afișat False deoarece a și b referă instanțe List 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.

Problemă rezolvată matrice clasa a XI-a mate-info neintensiv

Enunț

Fiind dată o matrice a cu m linii și n coloane cu elemente numere întregi, scrieți un program care:

1. Determină media aritmetică a elementelor matricii;
2. Afișează numărul elementelor pozitive, numărul elementelor negative și numărul elementelor nule din matrice;
3. Tipărește elementele pare aflate pe linii impare și coloane divizibile cu 3.

Rezolvare

#include
using namespace std;


const int maxn = 101;
const int maxm = 101;


int main()
{
 int N, M, A[maxm][maxn];
 int nrElPoz = 0, nrElNeg = 0,
  nrElNule = 0, contor = 0,
  ma = 0;


 ifstream in(„fisier.in”);
 in >> N >> M;
 for (int i = 1; i <= N; ++i)
 {
  for (int j = 1; j <= M; ++j)
  {
   in >> A[i][j];


   // adunarea numerelor pt. calculul
   // mediei aritmetice:
   ma += A[i][j];


   // cele 4 contoare:
   if (A[i][j] == 0)
   {
    ++nrElNule;
   }
   else if (A[i][j] > 0)
   {
    ++nrElPoz;
   }
   else // if (A[i][j] < 0)
   {
    ++nrElNeg;
   }


   if (A[i][j] % 2 == 0 && (i + 1) % 2 != 0 &&
    (j + 1) % 3 == 0)
   {
    ++contor;
   }
  }
 }
 ma /= N * M;
 in.close();


 ofstream out(„fisier.out”);
 out << "Media aritmetica: " << ma << endl;
 out << "Nr. elemente pozitive: " << nrElPoz << endl;
 out << "Nr. elemente nule: " << nrElNule << endl;
 out << "Nr. elemente negative: " << nrElNeg << endl;
 out << "Nr. elemente pare pe linii impare si coloane div. cu 3: " <<
  contor << endl;


 out.close();

 return 0;
}

Schemă

Notă: în unele locuri s-a referit la A[i][j] prin sintaxa A(i, j).

Explicații

Rezolvarea aceasta citește și scrie în fișier, nu pe ecran. Fișier de intrare: fisier.in, fișier de ieșire: fisier.out.

Cu excepția împărțirii sumei pentru aflarea mediei aritmetice, toate calculele se fac pe măsură ce se citește din fișierul de intrare.

Tutorial „Folosirea atributelor în C#”

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

Atributele oferă o cale de a asocia informații cu cod într-un mod declarativ. Ele pot de asemenea folosi un element reutilizabil care poate fi aplicat la o varietate de ținte.
Considerați atributul [Obsolete]. El poate fi aplicat claselor, structurilor, metodelor, constructorilor, și mai multe. El declară că elementul este învechit (en. obsolete). Apoi depinde de compilatorul C# să caute acest atribut, și să facă ceva ca răspuns.
In acest tutorial, dvs. veți fi introdus la cum să adăugați atribute codului dvs., cum să creați și să folosiți propriile dvs. atribute, și cum să folosiți unele atribute care sunt construite în .NET Core.

Cerințe preliminare

Dvs. va trebui să vă configurați mașina să ruleze .NET core. Puteți găsi instrucțiunile de instalare pe pagina .NET Core. Puteți rula această aplicație pe Windows, Ubuntu Linux, macOS sau într-un container Docker. Dvs. va trebui să vă instalați editorul favorit de cod al dvs. Descrierile de mai jos folosesc Visual Studio Code care este un editor cu sursă deschisă, cross-platformă. Totuși, puteți folosi oricare unelte cu care vă simțiți confortabil.

Crearea aplicației

Acum că v-ați instalat toate uneltele, creați o nouă aplicație .NET Core. Pentru a folosi generatorul din linie de comandă, executați următoarea comandă în shell-ul dvs. favorit:
dotnet new console
Această comandă va crea fișierele esențiale de proiect .NET core. Dvs. va trebui să executați dotnet restore pentru a restaura dependențele necesare pentru a compila acest proiect.
Notă. Începând cu .NET Core 2.0, nu trebuie să rulați dotnet restore deoarece este rulat în mod implicit de toate comenzile care cer o restaurare să aibă loc, cum sunt dotnet new, dotnet build și dotnet run. Este încă o comandă validă în unele scenarii unde a face o restaurare explicită are loc, cum ar fi construcții de integrare continuă în Azure DevOps Services sau în sisteme de construcție care au nevoie să controleze în mod explicit timpul la care are loc restaurarea.
Pentru a executa programul, folosiți dotnet run. Ar trebui să vedeți ieșirea „Hello, World” către consolă.

Cum adăugați atribute la cod

In C#, atributele sunt clase care moștenesc de la clasa bază Attribute. Oricare clasă care moștenește de la Attribute poate fi folosită ca un fel de „etichetă” pe alte bucăți de cod. De exemplu, există un atribut numit ObsoleteAttribute. Acesta este folosit să semnaleze că codul este învechit și trebuie să nu mai fie folosit. Puteți plasa acest atribut pe o clasă, de exemplu, folosind paranteze drepte.
[Obsolete]
public class MyClass
{
}
Notați că în timp ce clasa se cheamă ObsoleteAttribute, este necesar să folosiți doar [Obsolete] în cod. Aceasta este o convenție pe care C# o urmează. Puteți folosi numele complet [ObsoleteAttribute] dacă doriți.

Când marcați o clasă ca învechită, este o idee bună să oferiți ceva informație despre de ce este învechită, și/sau ce să se folosească în schimb. Faceți aceasta oferind un parametru șir la atributul Obsolete.

[Obsolete(„ThisClass este invechita. Folositi ThisClass2 in schimb.”)]
public class ThisClass
{


}

Sirul este transmis ca un argument la un constructor ObsoleteAttribute, ca și când ați scrie var attr = new ObsoleteAttribute(„ceva sir”).

Parametrii către un constructor de atribut sunt limitați la tipuri simple/literali: bool, int, double, string, Type, enums, etc. și vectori de aceste tipuri. Nu puteți folosi o expresie sau variabilă. Sunteți liberi să folosiți parametrii poziționali sau numiți.

Cum vă creați propriul atribut

Crearea unui atribut este atât de simplu ca moștenind de la clasa bază Attribute.

public class MySpecialAttribute : Attribute
{


}

Cu ce e deasupra, pot acum folosi [MySpecial] (sau [MySpecialAttribute]) ca un atribut altundeva în codul de bază.

[MySpecial]
public class SomeOtherClass
{


}

Atributele din biblioteca de clase bază .NET precum ObsoleteAttribute delanșează unele comportamente în compilator. Totuși, orice atribut creați acționează doar ca metadate, și nu rezultă în execuția nici unui cod în clasa atribut. Depinde de dvs. să acționați pe baza acestor metadate altundeva în codul dvs. (mai multe despre aceasta mai în târziu în tutorial).

Există un „te-am prins” aici de care să aveți grijă. După cum s-a menționat mai sus, doar unele tipuri sunt permise să fie transmise ca argumente când se folosesc atribute. Totuși, când se creează un tip atribut, compilatorul C# nu vă va opri din a crea acești parametri. In exemplul de mai jos, am creat un atribut cu un constructor care compilează bine.

public class GotchaAttribute : Attribute
{
    public GotchaAttribute(Foo myClass, string str) {
       
    }
}


Totuși, dvs. nu veți putea să folosiți acest constructor cu sintaxa de atribut.

[Gotcha(new Foo(), „test”)] // nu compileaza
public class AttributeFail
{


}

Ce e mai sus va cauza o eroare de compilare precum

Attribute constructor parameter ‘myClass’ has type ‘Foo’, which is not a valid attribute parameter type
tradus „Parametrul constructorului de atribut ‘myClass’ are tipul ‘Foo’, care nu este un tip valid de parametru de atribut.”

Cum restricționați utilizarea atributului

Atributele pot fi folosite pe un anumit număr de „ținte”. Exemplele de mai sus le arată pe clase, dar ele pot de asemenea fi folosite pe:

  • ansamblu (Assembly)
  • clasă
  • constructor
  • delegat
  • enumerare
  • eveniment
  • câmp
  • parametru generic
  • interfață
  • metodă
  • modul
  • parametru
  • proprietate
  • valoare de întoarcere
  • structură
Când creați o clasă atribut, în mod implicit, C# vă va permite să folosiți acel atribut pe oricare din țintele posibile ale atributelor. Dacă doriți să restricționați atributul dvs. la anumite ținte, puteți să faceți aceasta folosind AttributeUsageAttribute pe clasa dvs. atribut. E drept, un atribut pe un atribut!
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class MyAttributeForClassAndStructOnly : Attribute
{
}


Dacă încercați să puneți atributul de mai sus pe ceva care nu este o clasă sau structură, veți primi o eroare de compilator ca Attribute ‘MyAttributeForClassAndStructOnly’ is not valid on this declaration type. It is only valid on ‘class, struct’ declarations.

public class Foo
{
    // daca atributul de mai jos era necomentat, ar fi cauzat o eroare de compilator
    // [MyAttributeForClassAndStructOnly]
    public Foo()
    { }
}

Cum folosiți atribute atașate la un element de cod

Atributele se comportă ca metadate. Fără o fortă exterioară, ele nu vor face nimic de fapt.

Pentru a le găsi și a acționa pe atribute, Reflection este în general necesar. Nu voi acoperi Reflection în profunzime în acest tutorial, dar ideea de bază este că Reflection vă permite să scrieți cod în C# care examinează alt cod.

De exemplu, dvs. puteți folosi Reflection pentru a obține informații despre o clasă:

TypeInfo typeInfo = typeof(MyClass).GetTypeInfo();
Console.WriteLine(„Numele calificat de asamblare al MyClass este ” + typeInfo.AssemblyQualifiedName);


Aceasta va afișa ceva precum: Numele calificat de asamblare al MyClass este ConsoleApplication.MyClass, attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Odată ce aveți un obiect TypeInfo (sau un MemberInfo, FieldInfo, etc.), dvs. puteți folosi metoda GetCustomAttributes. Aceasta va returna o colecție de obiecte Attribute. Dvs. puteți de asemenea folosi GetCustomAttribute și specifica un tip Attribute.

Aici este un exemplu de utilizare a GetCustomAttributes pe o instanță MemberInfo pentru MyClass (pe care am văzut-o mai devreme că are un atribut [Obsolete] pe ea).

var attrs = typeInfo.GetCustomAttributes();
foreach(var attr in attrs)
    Console.WriteLine(„Attribute pe MyClass: ” + attr.GetType().Name);


Aceasta va tipări în consolă: Attribute pe MyClass: ObsoleteAttribute. Incrcați să adăugați alte atribute la MyClass.

Este important să notați că aceste obiecte Attribute sunt inițializate leneș. Aceasta înseamnă, ele nu vor fi inițializate până când dvs. folosiți GetCustomAttribute sau GetCustomAttributes. Ele sunt de asemenea instanțiate de fiecare dată. Apelând GetCustomAttributes de două ori la rând va întoarce două instanțe diferite ale ObsoleteAttribute.

Atribute comune în biblioteca de bază de clase (BCL)

Atributele sunt folosite de multe unelte și cadre de lucru. NUnit folosește atribute precum [Test] și [TestFixture] care sunt folosite de executorul de teste NUnit. ASP.NET MVC folosește atribute precum [Authorize] și oferă un cadru filtrare a acțiunii (en. action filter Framework) pentru a realiza aspecte intersectoriale (en. cross-cutting concerns) pe acțiuni MVC. PostSharp folosește sintaxa de atribute pentru a permite programarea orientată pe aspecte în C#.
Aici sunt câteva atribute notabile construite în bibliotecile de clase de bază ale .NET Core:
  • [Obsolete]. Acesta a fost folosit în exemplele de mai sus, și trăiește în namespace-ul System. Este folositor în a furniza documentație declarativă despre cod de bază în schimbare. Un mesaj poate fi furnizat în forma unui șir, și un alt parametru boolean poate fi folosit pentru a escalada de la o avertizare de compilator la o eroare de compilator.
  • [Conditional]. Acest atribut este în namespace-ul System.Diagnostics. Acest atribut poate fi folosit la metode (sau clase atribut). Dvs. trebuie să transmiteți un șir la constructor. Dacă acel șir se potrivește cu o directivă #define, atunci oricare apel la acea metodă (dar nu metoda însăși) va fi ștearsă de compilatorul C#. Uzual aceasta este folosită pentru scopuri de depanare (diagnosticare).
  • [CallerMemberName]. Acest atribut poate fi folosit pe parametri, și trăiește în namespace-ul System.Runtime.CompilerServices. Acesta este un atribut care este folosit pentru a injecta numele metodei care apelează altă metodă. Uzual, acesta este folosit ca o cale de a elimina ‘șiruri magice’ când se implementează INotifyPropertyChanged în variate cadre de lucru de interfață grafică. Ca un exemplu:
public class MyUIClass : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private string _name;
    public string Name
    {
        get { return _name;}
        set
        {
            if (value != _name)
            {
                _name = value;
                RaisePropertyChanged();   // notati ca nu e nevoie de „Name” aici in mod explicit
            }
        }
    }
}
In codul de mai sus, dvs. nu trebuie să aveți un șir literal „Name”. Aceasta poate preveni bug-uri legate de greșeli de scriere și de asemenea ajută la o refactorizare/redenumire mai ușoară.

Sumar

Atributele aduc putere declarativă în C#. Dar ele sunt o formă de cod ca meta-date, și nu acționează prin ele însele.

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

Expresii C#

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

Expresiile sunt construite din operanzi și operatori. Operatorii unei expresii indică ce operații să se aplice operanzilor. Exemple de operatori includ +, , *, /, și new. Exemple de operanzi includ literali, câmpuri, variabile locale, și expresii.
Când o expresie conține operatori multipli, precedența operatorilor controlează ordinea în care operatorii individuali sunt evaluați. De exemplu, expresia x + y * z este evaluată ca x + (y * z) deoarece operatorul * are precedență mai mare decât operatorul +.
Când un operand are loc între doi operatori cu aceeași precedență, asociativitatea operatorilor controlează ordinea în care operațiile sunt realizate:
  • Cu excepția operatorilor de atribuire, toți operatorii binari sunt asociativi la stânga, aceasta însemnând că operațiile sunt realizate de la stânga la dreapta. De exemplu, x + y + z este evaluat ca (x + y) + z.
  • Operatorii de atribuire și operatorul condițional (?:) sunt asociativi la dreapta, însemnând că operațiile sunt realizate de la dreapta către stânga. De exemplu, x = y = z este evaluat ca x = (y = z).
Precedența și asociativitatea pot fi controlate folosind paranteze. De exemplu, x + y * z mai întâi multiplică y cu z și apoi adaugă rezultatul la x, dar (x + y) * z mai întâi adaugă x și y și apoi multiplică rezultatul cu z.
Majoritatea operatorilor pot fi supraîncărcați. Supraîncărcarea unui operator permite implementări definite de utilizator ale operatorului să fie specificate pentru operații în care unul sau ambii operatori sunt de un tip clasă sau struct definit de utilizator.

Cele ce urmează sumarizează operatorii C#, listând categoriile de operatori în ordinea precedenței de la cea mai mare la cea mai mică. Operatorii din aceeași categorie au precedență egală. In fiecare categorie este o listă de expresii din acea categorie împreună cu descrierea acelui tip de expresie.
  • Primar
    • x.m : Accesarea de membru
    • x(…) : Invocarea de metodă sau delegat
    • x[…] : Acces prin tablou sau indexator
    • x++ : Post-incrementare
    • x– : Post-decrementare
    • new T(…) : Creare de obiect sau delegat
    • new T(…){…} : Creare de obiect cu inițializator
    • new {…} : Inițializator de obiect anonim
    • new T[…] : Creare de tablou
    • typeof(T) : Obține obiectul Type pentru T
    • checked(x) : Evaluează expresia în context verificat
    • unchecked(x) : Evaluează expresia în context neverificat
    • default(T) : Obține valoarea implicită a tipului T
    • delegate {…} : Funcție anonimă (metodă anonimă)
  • Unar
    • +x : Identitate
    • -x : Negație
    • !x : Negație logică
    • ~x : Negație pe biți
    • ++x : Pre-incrementare
    • –x : Pre-decrementare
    • (T)x : Convertește x la tipul T în mod explicit
    • await x : Așteaptă asincron ca x să se termine
  • Multiplicativ
    • x * y : Înmulțire
    • x / y : Împărțire
    • x % y : Rest
  • Aditiv
    • x + y : Adunare, concatenare de șiruri, combinare de delegați
    • x – y : Scădere, ștergere delegat
  • Deplasare
    • x << y : Deplasare la stânga
    • x >> y : Deplasare la dreapta
  • Relațional și testare de tip
    • x < y : Mai mic decât
    • x > y : Mai mare decât
    • x <= y : Mai mic decât sau egal cu
    • x >= y : Mai mare decât sau egal cu
    • x is T : Intoarce true dacă x este un T, fals altfel
    • x as T : Intoarce x tipizat ca T, sau null dacă x nu este un T
  • Egalitate
    • x == y : Egal
    • x != y : Nu este egal
  • SI (en. AND) logic
    • x & y : SI întreg pe biți, SI boolean logic
  • SAU EXCLUSIV (en. XOR) logic
    • x ^ y : SAU EXCLUSIV întreg pe biți, SAU EXCLUSIV boolean logic
  • SAU (en. OR) logic
    • x | y : SAU întreg pe biți, SAU boolean logic
  • SI condițional
    • x && y : Evaluează y doar dacă x nu este false
  • SAU condițional
    • x || y : Evaluează y doar dacă x nu este true
  • Coalescența null (en. null coalescing)
    • x ?? y : Evaluează la y dacă x este null, la x altfel
  • Condițional
    • x ? y : z : Evaluează y dacă x este true, z dacă x este false
  • Atribuire sau funcție anonimă
    • x = y : Atribuire
    • x op= y : Atribuire compusă; operatori suportați sunt
      • *= /= %= += -= <>= &= ^= |=
    • (T x) => y : Funcție anonimă (expresie lambda)

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

Instrucțiuni C#

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

Acțiunile unui program sunt exprimate folosind instrucțiuni (en. statements). C# suportă câteva feluri diferite de instrucțiuni, o parte din ele sunt definite în termeni de instrucțiuni incorporate.

Un bloc permite multiple instrucțiuni să fie scrise în contexte unde o singură instrucțiune este permisă. Un bloc consistă într-o listă de instrucțiuni scrise între delimitatorii { și }.
Instrucțiunile de declarare sunt folosite pentru a declara variabile și constante locale.
Instrucțiunile expresie sunt folosite pentru a evalua expresii. Expresiile care pot fi folosite ca instrucțiuni includ invocarea metodelor, alocarea obiectelor folosind operatorul new, atribuirea folosind = și operatorii de atribuire compusă, operații de incrementare și decrementare folosind operatorii ++ și și expresiile await.

Instrucțiunile de selecție sunt folosite pentru a selecta una dintre mai multe instrucțiuni posibile pentru execuție bazat pe valoarea unei expresii. In acest grup sunt instrucțiunile if și switch.

Instrucțiunile de iterare sunt folosite să execute în mod repetat o instrucțiune incorporată. In acest grup sunt instrucțiunile while, do, for, și foreach.

Instrucțiunile de salt sunt folosite pentru a transfera controlul. In acest grup sunt instrucțiunile: break, continue, goto, throw, return, și yield.

Instrucțiunea trycatch este folosită pentru a prinde excepțiile care au loc în timpul execuției unui bloc, și instrucțiunea tryfinally este folosită pentru a specifica un cod de finalizare care este executat întotdeauna, fie că o excepție a avut loc, fie că nu.
Instrucțiunile checked și unchecked sunt folosite pentru a controla contextul de verificare de depășire pentru operații aritmetice și conversii de tip întreg.
Instrucțiunea lock este folosită pentru a obține blocarea (en. lock) de excludere mutuală pentru un obiect dat, a executa o instrucțiune, și apoi a elibera blocarea.
Instrucțiunea using este folosită pentru a obține o resursă, a executa o instrucțiune, și apoi a elimina acea resursă.
In cele ce urmează se listează felurile de instrucțiuni care pot fi folosite, și se oferă un exemplu pentru fiecare.
  • Declarare de variabilă locală:
static void Declaratii(string[] args)
{
    int a;
    int b = 2, c = 3;
    a = 1;
    Console.WriteLine(a + b + c);
}
  • Declarare de constantă locală:
static void DeclaratiiConstante(string[] args)
{
    const float pi = 3.1415927f;
    const int r = 25;
    Console.WriteLine(pi * r * r);
}
  • Instrucțiune expresie:
static void Expresii(string[] args)
{
    int i;
    i = 123;                // Instructiune expresie
    Console.WriteLine(i);   // Instructiune expresie
    i++;                    // Instructiune expresie
    Console.WriteLine(i);   // Instructiune expresie
}
  • Instrucțiune if:
static void InstructiuneIf(string[] args)
{
    if (args.Length == 0)
    {
        Console.WriteLine(„Nici un argument”);
    }
    else
    {
        Console.WriteLine(„Unul sau mai multe argumente”);
    }
}
  • Instrucțiune switch:
static void InstructiuneSwitch(string[] args)
{
    int n = args.Length;
    switch (n)
    {
        case 0:
            Console.WriteLine(„Nici un argument”);
            break;
        case 1:
            Console.WriteLine(„Un argument”);
            break;
        default:
            Console.WriteLine($”{n} argumente”);
            break;
    }
}
  • Instrucțiune while:
static void InstructiuneWhile(string[] args)
{
    int i = 0;
    while (i < args.Length)
    {
        Console.WriteLine(args[i]);
        i++;
    }
}

  • Instrucțiune do:
static void InstructiuneDo(string[] args)
{
    string s;
    do
    {
        s = Console.ReadLine();
        Console.WriteLine(s);
    } while (!string.IsNullOrEmpty(s));
}
  • Instrucțiune for:
static void InstructiuneFor(string[] args)
{
    for (int i = 0; i < args.Length; i++)
    {
        Console.WriteLine(args[i]);
    }
}

  • Instrucțiune foreach:
static void InstructiuneForeach(string[] args)
{
    foreach (string s in args)
    {
        Console.WriteLine(s);
    }
}

  • Instrucțiune break:
static void InstructiuneBreak(string[] args)
{
    while (true)
    {
        string s = Console.ReadLine();
        if (string.IsNullOrEmpty(s))
            break;
        Console.WriteLine(s);
    }
}

  • Instrucțiune continue:
static void InstructiuneContinue(string[] args)
{
    for (int i = 0; i < args.Length; i++)
    {
        if (args[i].StartsWith(„/”))
            continue;
        Console.WriteLine(args[i]);
    }
}
  • Instrucțiune goto:
static void InstructiuneGoTo(string[] args)
{
    int i = 0;
    goto verificare;
    repetare:
    Console.WriteLine(args[i++]);
    verificare:
    if (i < args.Length)
        goto repetare;
}
  • Instrucțiune return:
static int Aduna(int a, int b)
{
    return a + b;
}
static void InstructiuneReturn(string[] args)
{
   Console.WriteLine(Aduna(1, 2));
   return;
}

  • Instrucțiune yield:
static System.Collections.Generic.IEnumerable Interval(int deLa, int panaLa)
{
    for (int i = deLa; i < panaLa; i++)
    {
        yield return i;
    }
    yield break;
}
static void InstructiuneYield(string[] args)
{
    foreach (int i in Interval(-10,10))
    {
        Console.WriteLine(i);
    }
}
  • Instrucțiuni throw și try:
static double Imparte(double x, double y)
{
    if (y == 0)
        throw new DivideByZeroException();
    return x / y;
}
static void TryCatch(string[] args)
{
    try
    {
        if (args.Length != 2)
        {
            throw new InvalidOperationException(„Doua numere necesare”);
        }
        double x = double.Parse(args[0]);
        double y = double.Parse(args[1]);
        Console.WriteLine(Imparte(x, y));
    }
    catch (InvalidOperationException e)
    {
        Console.WriteLine(e.Message);
    }
    finally
    {
        Console.WriteLine(„La revedere!”);
    }
}
  • Instrucțiuni checked și unchecked:
static void CheckedUnchecked(string[] args)
{
    int x = int.MaxValue;
    unchecked
    {
        Console.WriteLine(x + 1);  // Depasire
    }
    checked
    {
        Console.WriteLine(x + 1);  // Exceptie
    }    
}
  • Instrucțiune lock:
class Cont
{
    decimal sold;
    private readonly object sync = new object();
    public void Extragere(decimal suma)
    {
        lock (sync)
        {
            if (suma > sold)
            {
                throw new Exception(
                    „Fonduri insuficiente”);
            }
            sold -= suma;
        }
    }
}
  • Instrucțiune using:
static void InstructiuneUsing(string[] args)
{
    using (TextWriter w = File.CreateText(„test.txt”))
    {
        w.WriteLine(„Rândul unu”);
        w.WriteLine(„Rândul doi„);
        w.WriteLine(„Rândul trei„);
    }
}

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

Structura unui program C#

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

Conceptele cheie organizaționale în C# sunt programele, spațiile de nume, tipurile, membrii, și  ansamblele. Programele C# consistă într-unul sau mai multe fișiere sursă. Programele declară tipuri, care conțin membri și pot fi organizați în spații de nume. Clasele și interfețele sunt exemple de tipuri. Câmpurile, metodele, proprietățile, și evenimentele sunt exemple de membri. Când programele C# sunt compilate, ele sunt fizic împachetate în ansamble. Ansamblele au în mod obișnuit extensia de fișier .exe sau .dll, depinzând de faptul că ele implementează aplicații sau, respectiv, biblioteci.

Exemplul declară o clasă numită Stack (ro. stivă) într-un spațiu de nume numit Acme.Collections:

using System;
namespace Acme.Collections
{
    public class Stack
    {
        Entry top;
        public void Push(object data)
        {
            top = new Entry(top, data);
        }


        public object Pop()
        {
            if (top == null)
            {
                throw new InvalidOperationException();
            }
            object result = top.data;
            top = top.next;
            return result;
        }
       
        class Entry
        {
            public Entry next;
            public object data;
            public Entry(Entry next, object data)
            {
                this.next = next;
                this.data = data;
            }
        }
    }
}


Numele complet calificat al acestei clase este Acme.Collections.Stack. Clasa conține câțiva membri: un câmp numit top, două metode numite Push și Pop, și o clasă îmbricată numită Entry. Clasa Entry conține mai departe trei membri: un câmp numit next, un câmp numit data, și un constructor. Presupunând că codul sursă al exemplului este stocat în fișierul acme.cs, linia de comandă

csc /t:library acme.cs

compilează exemplul ca o bibliotecă (cod fără un punct de intrare Main) și produce un ansamblu numit acme.dll.

Important. Exemplele de mai sus folosesc csc ca și compilator C# în linie de comandă. Acest compilator este un executabil Windows. Pentru a folosi C# pe alte platforme, dvs. ar trebui să folosiți uneltele pentru .NET Core. Ecosistemul .NET Core folosește interfața în linie de comandă dotnet pentru a organiza construcțiile în linie de comandă. Aceasta include organizarea dependențelor, și invocarea compilatorului C#. Vedeți acest tutorial pentru o descriere completă a acelor unelte pe platformele suportate de .NET Core.

Ansamblele conțin cod executabil în formă de instrucțiuni în Intermediate Language (IL) (ro. limbaj intermediar). Înainte de a fi executat, codul IL dintr-un ansamblu este automat convertit în cod specific procesorului de către compilatorul Just-In-Time (JIT) (ro. Exact-la-timp) al .NET Common Language Runtime (en. mediului de execuție comun limbajelor .NET).

Deoarece un ansamblu este o unitate autodescrisă de funcționalitate conținând și cod și metadate, nu este nevoie de directive #include și fișiere antet în C#. Tipurile și membrii publici conținuți într-un ansamblu particular sunt făcute disponibile într-un program C# pur și simplu prin referirea acelui ansamblu când se compilează programul. De exemplu, acest program folosește clasa Acme.Collections.Stack din ansamblul acme.dll:

using System;
using Acme.Collections;
class Example
{
    static void Main()
    {
        Stack s = new Stack();
        s.Push(1);
        s.Push(10);
        s.Push(100);
        Console.WriteLine(s.Pop());
        Console.WriteLine(s.Pop());
        Console.WriteLine(s.Pop());
    }
}

Dacă programul este stocat în fișierul example.cs, când example.cs este compilat, ansamblul acme.dll poate fi referit folosind opțiunea /r a compilatorului:

csc /r:acme.dll example.cs

Aceasta creează un ansamblu executabil numit example.exe, care, când este rulat, produce ieșirea:

100
10
1


C# permite textul sursă al unui program să fie stocat în câteva fișiere sursă. Când un program C# alcătuit din mai multe fișiere este compilat, toate fișierele sursă sunt procesate împreună, și fișierele sursă pot să se refere unele la celelalte în mod liber—conceptual, este ca dacă toate fișierele sursă ar fi fost concatenate într-un singur fișier mare înainte de a fi procesate. Declarațiile forward nu sunt niciodată necesare în C# pentru că, cu foarte puține excepții, ordinea declarațiilor este nesemnificativă. C# nu limitează un fișier sursă la declararea unui singur tip public nici nu cere ca numele fișierului sursă să se potrivească cu tipul declarat în fișierul sursă.

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

Tipuri și variabile C#

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

Există două feluri de tipuri în C#: tipuri valoare și tipuri referință. Variabilele de tipuri valoare își conțin datele direct în timp ce variabilele de tipuri referință stochează referințe către datele lor, cel din urmă fiind cunoscut ca obiecte. Cu tipuri referință, este posibil ca două variabile să se refere la același obiect și deci posibil ca operațiile asupra unei variabile să afecteze obiectul referit de cealaltă variabilă. Cu tipurile valori, variabilele fiecare au propria lor copie a datelor, și nu este posibil ca operațiile asupra uneia să afecteze alta (excepție făcând cazul variabilelor parametri ref și out).

Tipurile valoare C# sunt mai departe împărțite în tipuri simple, tipuri enum, tipuri struct, și tipuri valoare nulabile. Tipurile referință ale C# sunt mai departe împărțite în tipuri clasă, tipuri interfață, tipuri tablou (en. array), și tipuri delegat.

Cele ce urmează oferă o vedere de ansamblu a sistemului de tipuri al C#.
  • Tipuri valoare
    • Tipuri simple
      • întregi cu semn: sbyte, short, int, long
      • întregi fără semn: byte, ushort, uint, ulong
      • caractere Unicode: char
      • virgulă mobilă IEEE: float, double
      • decimal cu precizie înaltă: decimal
      • boolean: bool
    • Tipuri enum
      • Tipuri definite de utilizator de forma enum E {…}
    • Tipuri struct
      • Tipuri definite de utilizator de forma struct S {…}
    • Tipuri valoare nulabile
      • Extensii ale tuturor celorlalte tipuri valoare cu o valoare null
  • Tipuri referință
    • Tipuri clasă
      • Clasa primordială de bază a tuturor celorlalte tipuri: object
      • Siruri Unicode: string
      • Tipuri definite de utilizator de forma class C {…}
    • Tipuri interfață
      • Tipuri definite de utilizator de forma interface I {…}
    • Tipuri tablou
      • Uni- și multi-dimensional, de exemplu, int[] și int[,]
    • Tipuri delegat
      • Tipuri definite de utilizator de forma delegate int D(…)
Cele opt tipuri întregi oferă suport pentru valori de 8, 16, 32, și 64 de biți în formă cu semn sau fără semn.
Cele două tipuri în virgulă mobilă, float și double, sunt reprezentate folosind formatele IEC-60559 de precizie simplă (32 de biți) și, respectiv, precizie dublă (64 de biți).

Tipul decimal este un tip de date de 128 de biți potrivit pentru calcule financiare și monetare.

Tipul bool al C# este folosit pentru a reprezenta valori Booleane — valori care sunt fie true fie false.

Procesarea de caractere și șiruri în C# folosește codificarea Unicode. Tipul char reprezintă o unitate de cod UTF-16, și tipul string reprezintă o secvență de unități de cod UTF-16.

Aceasta sumarizează tipurile numerice ale C#.

  • Intregi cu semn
    • sbyte: 8 biți, interval de la -128 – 127
    • short: 16 biți, interval de la -32,768 – 32,767
    • int: 32 biți, interval de la -2,147,483,648 – 2,147,483,647
    • long: 64 biți, interval de la –9,223,372,036,854,775,808 la 9,223,372,036,854,775,807
  • Intregi fără semn
    • byte: 8 biți, interval de la 0 – 255
    • ushort: 16 biți, interval de la 0 – 65,535
    • uint: 32 biți, interval de la 0 la 4,294,967,295
    • ulong: 64 biți, interval de la 0 la 18,446,744,073,709,551,615
  • Virgulă mobilă
    • float: 32 biți, interval de la 1.5 × 10−45 – 3.4 × 1038, precizie de 7 cifre
    • double: 64 de biți, interval de la 5.0 × 10−324 – 1.7 × 10308, precizie de 15 cifre
  • Decimal
    • decimal: 128 biți, intervalul este cel puțin –7.9 × 10−28 – 7.9 × 1028, cu precizie de cel puțin 28 de cifre
Programele C# folosesc declarații de tip pentru a crea noi tipuri. O declarație de tip specifică numele și membrii noului tip. Cinci din categoriile de tipuri ale C# sunt tipuri definibile de utilizator: tipuri clasă, tipuri struct, tipuri interfață, tipuri enum, și tipuri delegat.

Un tip class definește o structură de date care conține membri date (câmpuri) și membrii funcții (metode, proprietăți, și altele). Tipurile clasă suportă moștenire singulară și polimorfism, mecanisme prin care clasele derivate pot extinde și specializa clase bază.

Un tip struct este similar cu un tip clasă în faptul că el reprezintă o structură cu membri date și membri funcții. Totuși, spre deosebire de clase, structurile sunt tipuri valoare și nu cer în mod obișnuit alocare pe heap. Tipurile structură nu suportă moștenire specificată de utilizator, și toate tipurile struct moștenesc implicit de la tipul object.

Un tip interface definește un contract ca o mulțime numită de membri funcții publici. O class sau o struct care implementează o interface trebuie să furnizeze implementări ale membrilor funcții ale interfeței. O interface poate moșteni de la multiple interfețe bază, și o class sau o struct pot implementa multiple interfețe.

Un tip delegate reprezintă referințe la metode cu o listă de parametri și tip de întoarcere particulare. Delegații fac posibilă tratarea metodelor ca entități care pot fi atribuite la variabile și transmise c parametri. Delegații sunt analogi cu tipurile funcții oferite de limbajele funcționale. Ele sunt de asemenea similare cu conceptul de pointeri de funcții găsit în câteva alte limbaje, dar spre deosebire de pointeri la funcții, delegații sunt orientați pe obiecte și tipuri sigure.

Tipurile class, struct, interface și delegate toate suportă programare generică, prin care pot fi parametrizate cu alte tipuri.

Un tip enum este un tip distinct cu constante numite. Oricare tip enum are un tip la bază, care trebuie să fie unul dintre cele opt tipuri întregi. Mulțimea de valori ale unui tip enum este aceeași ca mulțimea de valori a tipului de bază.

C# suportă tablouri uni- și multi-dimensionale de orice tip. Spre deosebire de tipurile listate mai sus, tipurile tablou nu trebuie să fie declarate înainte de a putea fi folosite. In schimb, tipurile tablou sunt construite prin urmarea unui nume de tip cu paranteze drepte. De exemplu, int[] este un tablou uni-dimensional de int, int[,] este un tablou bi-dimensional de int, și int[][] este un tablou uni-dimensional de tablouri uni-dimensionale de int.

Tipurile valori nulabile de asemenea nu trebuie să fie declarate înainte de a putea fi folosite. Pentru fiecare tip valoare non-nulabil T există un tip valoare nulabil T?, care poate reține o valoare în plus, null. De exemplu, int? este un tip care poate conține orice întreg de 32 de biți sau valoarea null.

Sistemul de tipuri al C# este unificat în așa fel încât o valoare de oricare tip poate fi tratată ca un object. Oricare tip din C# derivă direct sau indirect din tipul clasă object, și object este clasa bază finală a tuturor tipurilor. Valorile de tipuri referință sunt tratate ca obiecte pur și simplu prin vederea valorilor ca tip object. Valorile de tipuri valoare sunt tratate ca obiecte realizând operațiile boxing și unboxing. In următorul exemplu, o valoare int este convertită la object și înapoi din nou la int.

using System;
class BoxingExample
{
    static void Main()
    {
        int i = 123;
        object o = i;    // Boxing
        int j = (int)o;  // Unboxing
    }
}


Când valoarea unui tip valoare este convertită la tipul object, o instanță object, de asemenea numită o „cutie” (en. box), este alocată pentru a reține valoarea, și valoarea este copiată în acea cutie. Invers, când o referință object este convertită la un tip valoare, o verificare este făcută ca object-ul referit să fie o cutie a tipului valoare corect, și, dacă verificarea reușește, valoarea din cutie este copiată în afară.

Sistemul unificat de tipuri al C# înseamnă efectiv că tipurile valoare pot deveni obiecte „la cerere”. Datorită unificării, bibliotecile cu scop general care folosesc tipul object pot fi folosite și cu tipuri referință și cu tipuri valoare.

Există câteva feluri de variabile în C#, inclusiv câmpuri, elemente de tablouri, variabile locale, și parametri. Variabilele reprezintă locații de stocare, și fiecare variabilă are un tip care determină ce valori pot fi stocate în variabilă, după cum se arată dedesubt.

  • Tip valoare non-nulabil
    • O valoare a exact acelui tip
  • Tip valoare nulabil
    • O valoare null sau o valoare a exact acelui tip
  • object
    • O referință null, o referință la un obiect de oricare tip referință, sau o referință la o valoare boxed de oricare tip valoare
  • Tip clasă
    • O referință null, o referință la o instanță a acelui tip clasă, sau o referință la o instanță a unei clase derivate din acel tip clasă
  • Tip interfață
    • O referință null, o referință la o instanță a unui tip clasă care implementează acel tip interfață, sau o referință la o valoare boxed a unui tip valoare care implementează acel tip interfață
  • Tip tablou (en. array)
    • O referință null, o referință la o instanță a acelui tip tablou, sau o referință la o instanță a unui tip tablou compatibil
  • Tip delegat
    • O referință null sau o referință la o instanță a unui tip delegat compatibil

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

Problemă rezolvată șir de caractere clasa a XI-a mate-info neintensiv #6

Enunț

Se citește de la tastatură un șir de maxim 70 caractere. Să se afișeze, unul sub altul, caracterele distincte din șir împreună cu frecvențele lor de apariție. Pe fiecare rând se va afișa un caracter urmat de frecvența sa în șir (prin frecvența de apariție a unui caracter se înțelege de câte ori apare caracterul respectiv în șir). Exemplu: Dacă se citește șirul „abracadabra”, programul a afișa ceea ce apare în figură.

Rezolvare

#include
#include
using namespace std;


int main()
{
 char s[71];
 int l;


 cout << "Introduceti un sir: ";
 cin >> s;


 l = strlen(s);

 char caractere[30];
 int lCaractere = 0;


 int frecvente[30];

 for (int i = 0; i < l; ++i)
 {
  bool existaDeja = false;
  for (int j = 0; j < lCaractere; ++j)
  {
   if (caractere[j] == s[i])
   {
    existaDeja = true;


    ++frecvente[j];

    break;
   }
  }


  if (!existaDeja)
  {
   ++lCaractere;
   caractere[lCaractere – 1] = s[i];
   frecvente[lCaractere – 1] = 1;
  }
 }


 for (int i = 0; i < lCaractere; ++i)
 {
  cout << caractere[i] <   ” << frecvente[i] << endl;
 }


 return 0;
}

Vizual

Explicații

O implementare alternativă ar folosi clasa map din S.T.L.