Назад Вперед Зміст

Приклад із трикутником

Спроектуємо клас для опису трикутників. Причому потрібно дати можливість клієнту ввести три сторони і отримати інформацію про периметр та площу.

Пишемо у клас Triangle, закриємо всі поля і методи, крім MyTriangle. Логічний метод Exists перевіряє існування трикутника.

Метод Exists обов’язково потрібно викликати у конструкторі.

Повний клас:

internal class Triangle
{
    private Double a, b, c; 
    private bool isTriangle = false;

    public Triangle(double a, double b, double c)
    {
        this.a = a; 
        this.b = b; 
        this.c = c;
        if (this.Exist()) 
        {
            this.isTriangle = true;
        }
    }

    private bool Exist()
    {
        return (a + b > c) && (a + c > b) && (b + c > a);
    }

    private Double Perimetr() 
    { 
        return this.a + this.b + this.c; 
    }

    private Double Area()
    {
        double p = this.Perimetr() / 2;
        return Math.Sqrt(p * (p - a) * (p - b) * (p - c));
    }

    public String MyTriangle()
    {
        Double S = this.Area();
        Double P = this.Perimetr();
        return (isTriangle) 
            ? $"Трикутник зі сторонами {a}, {b}, {c}, \n площа = {S}, \n периметр = {P}"
            : $"Трикутник зі сторонами {a}, {b}, {c} не існує.";
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyTriangle
{
    class Program
    {
        static void Main(string[] args)
        {
            Triangle mytriangle1 = new Triangle(3, 4, 5);
            Console.WriteLine(mytriangle1.MyTriangle());
            Triangle mytriangle2 = new Triangle(300, 4, 5);
            Console.WriteLine(mytriangle2.MyTriangle());
            Console.ReadKey();
        }
    }
}

Код Triangle 3

ЦЕ ЩЕ НЕ ВСЕ ПРО ІНКАПСУЛЯЦІЮ!!!

Якщо потрібен доступ до закритих полів класу — допоможуть методи доступу до полів (гетери, сетери), властивості (аксесори).

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

Доступ до полів класу з інших класів

Зазвичай поля класу описують з модифікатором доступу privat. Цей модифікатор можна не писати, він встановлюється за умовчанням.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace triangle
{
    class Triangle
    {
        private double a;
        private double b;
        private double c;

        public Triangle(double x, double y, double z)
        {
            a = x;
            b = y;
            c = z;
        }

        public void Print()
        {
            Console.WriteLine("Сторони: a={0}, b={1}, c={2}", a, b, c);
        }
    }
}

Тому доступ до таких полів з інших класів неможливий. Будь-яка спроба прочитати значення з цього поля або записати щось, викликає помилку.

// Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace triangle
{
    class Program
    {
        static void Main(string[] args)
        {
            double x = 3, y = 4, z = 5;
            Triangle t = new Triangle(x, y, z);
            t.SetSideC(7);  // Використовуємо метод для зміни сторони (замість прямого доступу до поля)
            Console.WriteLine(t.GetSideC());  // Отримуємо значення сторони через метод
            t.Print();
            Console.ReadKey();
        }
    }
}

"triangle.Triangle.ct недоступний через рівень захисту" — виникає при спробі прямого доступу до приватного поля. Виправлено шляхом додавання методів-геттерів/сеттерів.


// Triangle.cs
namespace triangle
{
    class Triangle
    {
        private double a;
        private double b;
        private double c;  // Приватне поля (недоступні ззовні)

        public Triangle(double x, double y, double z)
        {
            a = x;
            b = y;
            c = z;
        }

        // Методи для доступу до сторони 'c'
        public double GetSideC() => c;
        public void SetSideC(double value) => c = value;

        public void Print()
        {
            Console.WriteLine($"Сторони: a={a}, b={b}, c={c}");
        }
    }
}

Властивості класу

Але часто доступ до полів потрібний. Для цього використовуються властивості. Властивості ще називають методи аксесори.

Напишемо властивості для кожного поля цього класу.

class Triangle
{
    private double a;
    private double b;
    private double c;

    public Triangle(double x, double y, double z)
    {
        a = x;
        b = y;
        c = z;
    }

    // Властивість для сторони A
    public double A
    {
        get { return a; }
        set { a = value; }
    }

    // Властивість для сторони B
    public double B
    {
        get { return b; }
        set { b = value; }
    }

    // Властивість для сторони C
    public double C
    {
        get { return c; }
        set { c = value; }
    }
}

Як бачимо, кожна властивість має заголовок і тіло.Відповідно до конвенцій C# — властивості називаються так само, як і поля, але з великої літери. У заголовку вказується модифікатор доступу (зазвичай public), тип значення, що повертається властивістю (у нас double) та ім'я властивості. Ім'я властивості таке саме як ім'я поля, якому воно відповідає, тільки починається з великої літери.

У тілі оголошено два методи get та set. Більше нічого в тілі якості оголошувати не можна. Метод get має ключове слово return та повертає будь-яке значення (зазвичай значення поля). Якщо цей метод є присутнім, то значення відповідного поля можна читати.

Метод set має ключове слово value і надає (встановлює) це значення полю об'єкта. Якщо цей метод є присутнім, то відповідному полю можна надавати значення.

У властивості може бути відсутнім get або set. Тоді поле буде лише для читання або лише для запису.

Описані вище властивості можна використовувати в іншому класі, і це не викликає жодної помилки. Звернення до властивості схоже на звернення до методу, тільки круглі дужки, як у методі, не пишуть.

class Program
{
    static void Main(string[] args)
    {
        double x = 3, y = 4, z = 5;
        Triangle t = new Triangle(x, y, z);
        
        // Змінюємо сторону A через властивість
        t.A = 7;
        
        // Виводимо сторону C через властивість (замість прямого доступу до ct)
        Console.WriteLine(t.C);
        
        // Виводимо інформацію про трикутник
        t.Print();
        
        Console.ReadKey();
    }
}

Властивості разом із модифікаторами доступу реалізують механізм захисту даних від несанкціонованого доступу.

Рекомендації з програмування

Клас як тип, визначений користувачем, повинен містити:

  1. Закриті (private) поля;
  2. Конструктор без параметрів, що надає полям нульові значення;
  3. Конструктор з параметрами, що надає полям значення параметрів;
  4. Методи-аксесори (так інакше називають властивості) для всіх полів;
  5. Набір методів, що реалізують поведінку класу. Для доступу до методів з інших класів їх потрібно описувати зі специфікатором public.

    Властивостям властиві низка суттєвих обмежень:

    1. Властивість не визначає місце для зберігання даних, тому її не можна передати методу як параметр ref або out.
    2. Властивість не підлягає перевантаженню. Наявність двох різних властивостей з доступом до однієї і тієї ж змінної допускається, але це радше виняток, ніж правило.
    3. Властивість не повинна змінювати стан базової змінної при виклику аксесора get. Хоча це обмеження не контролюється компілятором, порушення вважається семантичною помилкою. Дія аксесора get не повинна втручатися у функціонування змінної.

    Автоматично реалізовані властивості

    Починаючи з версії C# 3.0, з’явилась можливість реалізувати дуже прості властивості без явного визначення змінної, якою керує властивість. Натомість базову змінну для властивості автоматично створює компілятор. Такі властивості називають автоматично реалізованими і вони мають наступну загальну форму:

    тип ім'я { get; set; }

    де тип позначає конкретний тип властивості, а ім'я — ім'я властивості. Зверніть увагу, що після get і set йде крапка з комою, а тіла відсутні. Цей синтаксис наказує компілятору створити автоматичну змінну, яку іноді називають полем підтримки, для збереження значення. Ця змінна недоступна напряму і не має імені, але доступна через властивість.

    У чому перевага автосвойств, якщо вони по суті просто звертаються до автоматично створеної змінної? Чому б не звертатися напряму до змінної без автосвойств? Справа в тому, що в будь-який момент при необхідності ми можемо розгорнути автосвойство у звичайне, додати у нього певну логіку.

    Варто враховувати, що неможливо створити автоматичну властивість тільки для запису, як у випадку зі стандартними властивостями.

    Приклад із трикутником:

    internal class Triangle
    {
        public double A { get; private set; }
        public double B { get; private set; }
        public double C { get; private set; }
    
        public double Perimeter
        {
            get { return A + B + C; }
        }
    
        public double Area
        {
            get 
            { 
                double p = Perimeter / 2;
                return Math.Sqrt(p * (p - A) * (p - B) * (p - C));
            }
        }
    
        private bool Exist()
        {
            return (A + B > C) && (A + C > B) && (B + C > A) 
                   && (A> 0) && (B > 0) && (C > 0);
        }
    
        public Triangle(double a, double b, double c)
        {
            A = a;
            B = b;
            C = c;
            if (!Exist()) 
                throw new Exception("Трикутник з такими сторонами не існує");
        }
    }
    using System;
    
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Введіть три сторони трикутника:");
                double a = Convert.ToDouble(Console.ReadLine());
                double b = Convert.ToDouble(Console.ReadLine());
                double c = Convert.ToDouble(Console.ReadLine());
                
                Console.WriteLine($"Конструктор з трьома параметрами. Три сторони трикутника: {a}, {b}, {c}.");
                Triangle mytriangle = new Triangle(a, b, c);
                
                Console.WriteLine($"Периметр = {mytriangle.Perimeter}");
                Console.WriteLine($"Площа = {mytriangle.Area}");
                
                Console.WriteLine("Введіть три сторони трикутника:");
                a = Convert.ToDouble(Console.ReadLine());
                b = Convert.ToDouble(Console.ReadLine());
                c = Convert.ToDouble(Console.ReadLine());
                
                Triangle mytriangle2 = new Triangle(a, b, c);
                Console.WriteLine($"Конструктор з трьома параметрами. Три сторони трикутника: {mytriangle2.A}, {mytriangle2.B}, {mytriangle2.C}.");
                
                Console.WriteLine($"Периметр = {mytriangle2.Perimeter}");
                Console.WriteLine($"Площа = {mytriangle2.Area}");
            }
            catch
            {
                Console.WriteLine("Трикутник не існує.");
            }
        }
    }

    Приклад коду 3


    Назад Вперед Зміст