Класи та об'єкти

Клас – основний тип даних в мові C#. Клас являє собою конструкцію, яка об’єднує поля, властивості та методи. Клас є визначенням для створення об’єктів або екземплярів класу.

Синтаксис оголошення:

[modifiers] class ClassName
{
    //тіло класу
}

Спочатку йде модифікатор доступу, а після ключового слова class, ім’я класу, в фігурних дужках знаходиться тіло класу, яке може містити поля, властивості та методи – члени класу.

Простий приклад оголошення класу для зберігання даних користувача:

public class User
{
    public string UserName;
    public byte UserAge;
}

Для створення екземпляру класу використовується оператор new:

User u = new User();

Члени класу

Клас може містити в собі наступні члени:

  • Конструктори;
  • Константи;
  • Поля;
  • Методи;
  • Властивості;
  • Оператори;
  • Вкладені типи даних;
  • Деструктори.

Доступ до членів екземпляру класу здійснюється через оператор “.”, наприклад u.UserAge = 21;

Модифікатори доступу

Модифікатор доступу – визначає звідки можна звертатись до класу чи його членів.

В мові C# реалізовані наступні рівни доступу:

  • public – максимально доступний рівень, не накладає жодних обмежень;
  • protected – доступ дозволено з поточного класу чи його спадкоємців;
  • internal – доступ обмежено поточною збіркою(в межах програми, бібліотеки);
  • protected internal – комбінація двох попередніх модифікаторів;
  • private – доступ дозволено тільки в межах даного класу;
  • private protected – доступ дозволено в поточному класі та класах нащадках, які містяться в цій збірці.

Якщо для класу чи його членів не задані модифікатори доступу, то встановлюється модифікатор за замовчуванням internal для класу та private для членів.

Раніше ми вже стикалися з модифікаторами доступу і використовували їх при створенні методів.

Конструктор

Конструктор – це спеціальний метод, який викликається при створенні нового екземпляру класу, він виділяє память необхідну для зберігання об’єкта, та як правило, виконує ініціалізацію полів та властивостей. Ім’я конструктора повинно бути ідентичним імені класу. Якщо в класі немає конструктора, то компілятор генерує конструктор за замовчуванням без параметрів.

Приклад конструктора класу:

public class TrackPoint
{
    //публічні поля
    public float X;
    public float Y;

    //конструктор
    public TrackPoint(float x, float y)
    {
        //ініціалізація полів
        X = x;
        Y = y;
    }
}

Зараз при створенні об’єкта класу TrackPoint необхідно передавати в конструктор аргументи:

var tp = new TrackPoint(2f, 3f);

Клас може містити скільки завгодно конструкторів, які створюються за аналогією з перевантаженням методів:

public class RGBColor
{
    //публічні поля
    public int Red;
    public int Green;
    public int Blue;

    //конструктор без параметрів
    public RGBColor()
    {
    }

    //конструктор з опціональними параметрами
    public RGBColor(int r, int g = 0, int b = 0)
    {
        Red = r;
        Green = g;
        Blue = b;
    }
}

Створення екземплярів класу:

var c1 = RGBColor();
var c2 = RGBColor(10, 20);

Починаючи з 7.0 версії мови C# конструктор з одним виразом можна записати в скороченій формі:

public class Dog
{
    public string Name;
    public uint Weight;

    //скорочена форма запису
    public Dog(string n) => Name = n; //в конструкторі тільки один вираз
    public Dog(string n, uint w) => SetParameters(n, w); //конструктор викликає метод(це теж одна операція)

    private void SetParameters(string n, uint w)
    {
        Name = n;
        Weight = w;
    }
}

Ключове слово this

this – вказує на поточний екземпляр класу. Зазвичай використовується для розділення параметрів конструктора від полів з такими ж назвами:

public class Worker
{
    private string workerName;

    public Worker(string workerName)
    {
        //полю класу буде присвоєно значення аргументу конструктора
        this.workerName = workerName;
    }
}

Також ключове слово this може використовуватись для виклику конструктору класу:

protected class BlogPage
{
    string title;
    uint year;

    //викликає другий конструктор з рядковим аргументом
    protected BlogPage() : this("Draft page") 
    {
    }

    //викликає третій, передаючи заголовок та рік по замовчуванню
    protected BlogPage(string title) : this(title, 2019) 
    {
    }

    protected BlogPage(string title, uint year)
    {
        this.title = title;
        this.year = year;
    }
}

Створення екземплярів BlogPage:

var page1 = new BlogPage(); //заголовок - Draft page, рік 2019
var page2 = new BlogPage("My Site");// My Site 2019
var page3 = new BlogPage("First article", 2018);// First article 2018

Поля та константи

Поля класу – це змінні які оголошені всередині класу. Не рекомендується використовувати публічні поля, доступ до них повинен здійснюватися за допомогою властивостей та методів, а ініціалізація зазвичай реалізується в конструкторі.

Використовуйте поля з модифікаторами доступу private чи protected, використання public полів є поганою практикою.

Константи – це ідентифікатори, значення яких задається під час компіляції програми, та не може змінюватись в процесі виконання додатку.

internal class CircleFigure
{
    //константа класу
    internal const double Pi = 3.1415;
    //приватне поле
    private double r;

    internal CircleFigure(double radius)
    {
        r = radius;
    }

    //отримання даних поля з використанням методу
    internal double GetRadius() => r;

    //метод для обчислення площі кола
    internal double CalculateArea() => Pi * r * r;
}

Властивості

Властивість – це член, який надає механізм читання і запису даних в поле класу. Зовні властивості не відрізняються від полів, проте вони містять спеціальні методи доступу, які викликаються для читання і зміни даних.

Характеристики властивостей:

  • Властивості забезпечують доступ до даних, при цьому приховують механізм перевірки та отримання значень;
  • Метод get - викликається при читанні значення властивості, може містити обробку даних перед поверненням;
  • Метод set - викликається при присвоєнні значення властивості, може використовуватися для перевірки значення. У тілі set доступно ключове слово value, яке містить присвоєне властивості значення;

Використовуючи варіанти get і set можна створювати різні рівні доступу:

  • get і set – читання та запис;
  • get – тільки читання;
  • set – тільки запис.

Розглянемо приклад програми для обчислення площі прямокутника:

using System;

public class Rectangle
{
    //приватні поля
    private uint a;
    private uint b;
    private string n;

    //сторона А
    public uint SideA
    {
        get { return a; }
        set
        {
            //перевірка значення перед присвоєнням
            if (value > 0)
                a = value;
            else
                Console.WriteLine("Помилка А == 0");
        }
    }

    //сторона В
    public uint SideB
    {
        get { return b; }
        set
        {
            //перевірка значення перед присвоєнням
            if (value > 0)
                b = value;
            else
                Console.WriteLine("Помилка В == 0");
        }
    }

    //назва прямокутника
    //властивість з доступом тільки для запису
    public string Name
    {
        set { n = value; }
    }

    //площа прямокутника
    //властивість з доступом тільки для читання
    public uint Area
    {
        //обробка перед поверненням
        get { return a * b; }
    }

    //метод виводить на екран дані про фігуру
    public void Print()
    {
        Console.WriteLine($"{n} {a}x{b} Area = {Area}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var r = new Rectangle();
        r.Name = "ABCD";
        r.SideA = 0; //Виводиться повідомлення "Помилка А == 0"
        r.SideA = 9;
        r.SideB = 7;
        r.Print(); //ABCD 9x7 Area = 63
        Console.ReadLine();
    }
}

C# підтримує ініціалізацію полів та властивостей класу при створенні об’єкта:

var rec = new Rectangle { Name = "NMKL", SideA = 2, SideB = 4 };

Для більш короткого запису можна створювати автоматично реалізовані властивості або автовластивості, для яких не задані приватні поля:

protected string ComputerName { get; set; }

Починаючи з C# версії 6.0, автовластивості можна ініціалізувати подібно до полів:

protected int CurrentYear{ get; set; } = 2019;

В тій же версії мови стала доступна скорочена форма запису властивостей тільки для читання:

public class Human
{
    private string firstName;
    private string lastName;
   
    public Human(string first, string last)
    {
       firstName = first;
       lastName = last;
    }

    //властивість з доступом для читання
    public string FullName => $"{firstName} {lastName}";   
}

В C# версії 7.0 методи get і set можна записувати з використанням оператора визначення тіла виразу=>“:

private ulong t;
public ulong TotalCount
{
  get => t;
  set => t = value; 
}

Вкладені типи даних

Клас може містити в собі інші класи(nested classes), структури та перерахування.

Розглянемо клас з вкладеними типами:

internal class MainClass
{
    internal enum SelectedColor
    {
        Black,
        White,
        Gray
    }

    internal class Circle
    {
        internal double Radius {get; set;}
    }
}

Для створення екземплярів вкладених типів даних до них звертаються як і до інших членів класу:

var c = new MainClass.Circle { Radius = 10 };
var w = MainClass.SelectedColor.White;

Правила іменування

Імена класів та його членів можуть бути будь-якими, однак для кращої читабельності коду рекомендується використовувати Camel Case та Pascal Case нотації.

PascalCase - це стиль написання імен, при якому складові слова назви імені пишуться разом, а кожне нове слово починається з великої літери. Приклад: UserAgent, CalculatePerimeter, UserArray. Використовується для назв класів, констант, публічних полів і властивостей, а також іменування всіх методів.

CamelCase (верблюжа нотація) - стиль повторює паскаль нотацію, тільки починається з маленької літери. Приклад: tempValue, cycleCounter. Використовується для локальних змінних і констант.

Всім членам бажано давати імена, що описують функціонал за який вони відповідають.

Дивіться також: