Սովորում ենք C# | Struct և Class
Tags: dotnet, csharp, beginners, armenian
Published: 2020-04-12 09:39:38

Նախորդ գրառումներում արդեն իսկ ծանոթացել ենք .NET բազային գրադարանի կողմից առաջարկվող տիպերից մի քանիսին և ինչպես են դրանք պահվում հիշողության մեջ (հիշեցնեմ, որ ըստ պահպանման ձևի, տիպերը լինում են երկու տեսակի՝ արժեքային և հղումային․ դրանց պահպանման մանարամասների մասին խոսել եմ [նախորդ գրառման մեջ](https://dev.to/n_babajanyan/c-stack-heap-9ff))։ Այժմ ժամանակն է տեսնել թե ինչպես կարող ենք ստեղծել մեր սեփական տիպերը, խնդիրների առավել արագ լուծման համար։ 

Պատկերացնենք, որ ունենք երկրաչափական կետերի հետ աշխատող ծրագիր գրելու հանձնարարություն։ Երկչափ հարթության մեջ կետը կարելի է պահել երկու առանձին փոփոխականների տեսքով, X և Y կորդինատների արժեքների համար։

Այս դեպքում, երկու կետերի հեռավորություն հաշվող մեթոդը կունենա հետևյալ տեսքը՝
```c#
public double calculateDistance(double x1, double y1, double x2, double y2)
{
    return Math.sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
}
```
Իսկ ի՞նչ եթե կետերը գտնվեին եռաչափ տարածության մեջ, այսինքն ամեն կետի ներկայացման համար պահանջվեր երեք փոփոխական։ Իսկ եթե պահանջվեր գտնել ոչ թե երկու կետի հեռավորություն, այլ անհայտ քանակով կետերից կազմված պատկերի մակերե՞ս։ Այդ դեպքում ստիտպված կլինեինք օգտվել մի քանի զանգվածից (բոլոր կետերի X և Y կորդինատների համար)։
Տվյալ դեպքում արդյունավետ լուծման համար մեզ անհրաժեշտ է ստեղծել նոր տիպ, որն իր մեջ կպարունակի կետի մասին հիմնական տվյալները, ինչպես նաև դրանց հետ աշխատելող մեթոդները։

### Struct
C# լեզվում struct-ը տիպերի կատեգորիա է, որը պատկանում է արժեքային տիպերի (*value type*) տեսակին, այսինքն մենք կարող ենք ստեղծել struct տեսակի սեփական տիպ, որը կլինի արժեքային։

Մեր ստեղծված տիպը կարող է պարունակել ինչպես փոփոխականներ (որոնք կոչվում են **դաշտեր**՝ *fields*), այնպես և մեթոդներ։

`struct`—ի միջոցով տիպ ստեղծելու համար անհրաժեշտ է նախ նշել `struct` բանալի բառը, այնուհետև մեր նոր տիպի անվանումը, որից հետո ձևավոր փակագծերի ներսում ներառել տիպի պարփակված փոփոխականներն ու մեթոդները։

Փորձենք ստեղծել նոր տիպ, երկրաչափական կետեր մոդելավորելու համար՝
```c#
struct Point 
{
    public double x;
    public double y;
}
```

Այս պարագայում, մեր հեռավորություն չափող մեթոդը կունենա հետևյալ տեսքը՝
```c#
public double calculateDistance(Point p1, Point p2)
{
    return Math.sqrt((p2.x - p1.x)*(p2.x - p1.x) + (p2.y - p1.y)*(p2.y - p1.y));
}
```

Ինչպես տեսնում ենք, այլևս կարիք չունենք օգտվելու առանձին փոփոխականներից․ դրա փոխարեն օգտագործում ենք երկու `Point` օբյեկտների ներսում պարփակված տվյալները։

Կարող ենք այս մեթոդը նույնպես ներառել `Point` տիպի մեջ՝
```c#
struct Point 
{
    public double x;
    public double y;

    public double calculateDistance(Point other)
    {
        return Math.sqrt((x - other.x)*(x - other.x) + (y - other.y)*(y - other.y));
    }
}
```
Երբ `calculateDistance()` մեթոդում նշում ենք `x` կամ `y`, օգտագործվում են այն օբյեկտում գտնվող փոփոխականները, որի համար կանչվել էր մեթոդը։ Այսինքն, եթե մեթոդը կանչվի հետևյալ կերպ՝
```c#
double d = p1.calculateDistance(p2);
```
ապա մեթոդի ներսում `x` և `y` փոփոխականները կպարունակեն p1 օբյեկտի կորդինատները, իսկ `other` փոփոխականը՝ p2 օբյեկտը, որից էլ կվերցվեն վերջինիս կորդինատները։

> Ուշադրություն դարձրեք միջակետի օպերատորին՝ `.`, որը կոչվում է **member access operator**։ Դրա միջոցով հնարավոր է դառնում աշխատել օբյեկտի ներսում պարփակված փոփոխականի հետ կամ կանչել դրա ներսում գտնվող մեթոդ։ Անհրաժեշտ է նշել օբյեկտը պարունակող փոփոխականի անվանումը, դնել միջակետ, որից հետո նշել անհրաժեշտ փոփոխականի կամ մեթոդի անունը՝ օրինակ `p1.x`, `p1.calculateDistance(p2)`։

Ինչպես արդեն նշվեց, `struct`-երի միջոցով կարելի է նախագծել սեփական տիպեր։ Իսկ կոնկրետ ինչպե՞ս են ստեղծվում մեր նոր տիպի փոփոխականներ։

#### Կոնստրուկտոր
Որևէ տիպի օբյեկտն այլ կերպ անվանում են այդ տիպի **նմուշ** (*instance*)։ Մեր դիտարկած օրինակում `Point`-ը տիպ է, իսկ `p1`-ն ու `p2`-ը՝ վերջինիս նմուշներ։ Եթե տիպն ընդունենք որպես հատակագիծ, ապա նմուշը հանդիսանում է ըստ այդ հատակագծի կառուցված մեկ շինություն։ Տիպում նախատեսված փոփոխականները (օրինակ `x` և `y`) դեռևս չեն հայտարարվում հիշողության մեջ, փոխարենը դա արվում է ամեն նմուշի համար առանձին՝ վերջինիս ստեղծման պահին։

Նոր նմուշների ստեղծման համար C# լեզվում օգտագործվում են հատուկ մեթոդներ, որոնք կոչվում են կոնստրուկտորներ (*constructor*)։ Մեթոդը հատուկ է նրանով, որ այն չունի վերադարձվող փոփոխականի տիպ (*return type*) և պարտադիր ունի նույն անունն ինչ տիպը։

Կոնստրուկտորի միջոցով նոր նմուշ ստեղծելու համար անհրաժեշտ է նշել `new` բանալի բառը, որից հետ կանչել կոնստրուկտոր մեթոդը՝ տալով անհրաժեշտ պարամետրերը (եթե դրանք նախատեսված են)
```c#
Point p = new Point();

// Այժմ p փոփոխականը պարունակում է Point տիպի օբյեկտ, որի տվյալների հետ կարող ենք աշխատել

p.x = 5.0d;
p.y = 0.0d;

p.x++;
// ...
```

Եթե կոնստրուկտորը չի ընդունում ոչ մի պարամետր, այն կոչվում է **գործարանային կոնստրուկտոր** (*default constructor*) և հասանելի է օգտագործման համար, եթե տիպում ոչ մի այլ կոնստրուկտոր հայտարարված չէ։

Վերոնշյալ օրինակում, մենք առաջին հերթին հայտարարում ենք `Point` տիպի փոփոխական, վերագրում նրան նույն տիպի նորաստեղծ օբյեկտ, այնուհետև արժեքներ տալիս դրա պարփակված փոփոխականներին։

Օբյեկտի ստեղծումը և փոփոխականների արժևորումը հնարավոր է համատեղել՝ կոնստրուկտորում նախատեսելով **պարամետրեր**։
```c#
struct Point 
{
    public double x;
    public double y;

    public Point(double xCoord, double yCoord) 
    {
        x = xCoord;
        y = yCoord;
    }
}
```

Այս նոր կոնստրուկտորը կիրառելիս, ծրագրավորողի կողմից տրված `xCoord` և `yCoord` պարամետրերի արժեքները վերագրվում են համապատասխանաբար `x` և `y` փոփոխականներին։
Հիշեցնեմ, որ `Point(double xCoord, double yCoord)` կոնստրուկտորը ներառելիս, մենք զրկվում ենք ավտոմատ ներառվող **գործարանային կոնստրուկտորից**։ Struct-երում գործարանային կոնստրուկտորի ավելացումը ձեռքով **արգելվում է**։

> **Նշում՝** `struct` տիպի օբյեկտ կարելի է ստեղծել նաև առանց կոնստրուկտորի օգտագործման։ Քանի որ տվյալ տիպն արժեքային է, դրա ցանկացած փոփոխական արդեն իսկ պարունակում է նորաստեղծ օբյեկտ։ Այսինքն, հնարավոր է նոր օբյեկտ ստեղծել և դրա հետ աշխատել հետևյալ կերպ՝

```c#
// Հայտարարում ենք փոփոխական առանց արժևորման,
// և այն արդեն իսկ պարունակում է Point տիպի նոր օբյեկտ
Point p; 
p.x = 13.0;
p.y = -1.5;
```

### Class

Ժամանակակից ծրագրավորման լեզուներում շատ մեծ ուշադրություն է դարձվում կլասսերին և դրանց հետ աշխատանքին։ Կլասսերը հանդիսանում են struct-երի առավել բազմաֆունկցիոնալ տեսակը։ .NET հարթակում աշխատելիս class-երի և struct-երի միջև գոյություն ունի կարևոր մի տարբերություն՝ _class-ը պատկանում է **հղումային տիպերի** դասին_, այսինքն եթե ստեղծում ենք նոր տիպ, ապա այդ տիպի փոփոխականը պարունակում է հիշողության մեջ տվյալ կլասսի նմուշի (*instance*) հասցեն (եթե այն ստեղծված է)։ Այն դեպքերում, երբ հայտարարված է փոփոխական, սակայն ստեղծված չէ նմուշ, փոփոխականը կրում է `null` (*դատարկ հասցե*) հատուկ արժեքը, ինչը նշանակում է որ այն չի հղում հիշողության մեջ որևէ իրական հասցեի։

Մեր նախագծում (*project*) նոր կլասս ավելացնելու համար անհրաժեշտ է Visual Studio-ում օգտվել **Add new item** մենյուից։
![Նոր կլասսի ավելացում](https://dev-to-uploads.s3.amazonaws.com/i/l9soj43mcvghvep9pnp9.PNG)

Ինչպես և struct-երը, կլասսերը նույնպես կարող են պարունակել դաշտեր և մեթոդներ։ Դրանք նույնպես օժտված են կոնստրուկտորներով։
Ի տարբերություն struct տիպերի, կլասսերի նմուշի ստեղծման համար **պարտադիր է** կոնստրուկտորի օգտագործումը։

> Կոնստրուկտորի օգտագործումը պարտադիր է քանի որ, հանդիսանալով հղումային տիպի  նմուշ, կլասսի փոփոխականը պարունակում է միայն օբյեկտի հասցեն, ոչ թե ամբողջ օբյեկտը։ Այսինքն միայն փոփոխական հայտարարելիս (առանց արժևորման), հիշողության մեջ տեղ է հատկացվում օբյեկտի **հասցեի** պահպանման համար, մինչդեռ իրական օբյեկտը ստեղծվում է կոնստրուկտորի միջոցով։

Կլասսի հայտարարումը ներառում է 
* `class` բանալի բառը
* կլասսի անվանումը
* ձևավոր փակագծերի ներսում, կլասսի պարփակված անդամները

Visual Studio—ում **Add new item** գործողությունից օգտվելու դեպքում, կլասսի սկզբնական կոդը գեներացվում է ավտոմատ կերպով և մեզ մնում է աշխատել կլասսի ներքին կոդի վրա (փոփոխականների, մեթոդների ավելացում և այլն)։

###Ամփոփում
Այս անգամ ծանոթացանք struct և class տիպերի տեսակներին, ինչպես դրանցով կարող ենք ստեղծել մեր սեփական տիպերը և այնուհետև ստեղծել այդ տիպերի **նմուշներ**։ Հասկացանք թե ինչ է կատարվում երբ հայտարարում ենք մեր նոր տիպի փոփոխական և ինչպես են օգտագործվում կոնստրուկտորները։

Հաջորդ գրառման մեջ կմանրամասնեմ կլասսերի օգտագործումը, մի քանի կոնստրուկտորների ներառումը և կներկայացնեմ օբյեկտների հետ աշխատող ծրագրերի գրագետ մշակման սկզբունքները։