C#. Властивості. Аксесори get, set. Приклади класів, що містять властивості

C#. Властивості. Аксесори get, set. Приклади класів, що містять властивості


Зміст


1. Поняття властивості. Загальна форма. Аксесори get, set

Властивість – це спеціальне поле класу, що містить програмний код доступу до внутрішніх змінних (полів) класу чи обчислення деякого значення. Властивості забезпечують зручний та раціональний шлях доступу до внутрішньої змінної в класі.

Загальна форма властивості у класі

type PropName {
    get {
        // код для читання внутрішнього поля (змінної)
        // ...
    }
    set {
        // код для запису значення у внутрішнє поле (змінну)
        // ...
    }
}

де

  • type – тип, який повертає властивість;
  • PropName – ім’я властивості. За цим іменем йде звертання до властивості;
  • get – аксесор, що використовуєтья для читання значення з внутрішнього поля класу;
  • set – аксесор, що використовується для запису значення у внутрішнє поле класу. Аксесор set отримує неявний параметр value, що містить значення, яке присвоюється властивості.

Якщо ім’я властивості зустрічається у правій частині оператора присвоювання або у виразі

variable = obj.PropName; // get

тоді викликається аксессор get. Тут obj – ім’я екземпляру класу, в якому оголошена властивість PropName.

Якщо ім’я властивості зустрічається у лівій частині оператора присвоювання

obj.PropName = expression; // set

тоді викликається аксессор set. Тут obj – ім’я екземпляру класу, в якому оголошена властивість PropName.

 

2. Переваги використання властивостей у класах

Використання властивостей у класах дає наступні переваги:

  • забезпечується більш раціональний та природній доступ до внутрішніх змінних класу;
  • в коді аксесорів можна реалізовувати різного роду перевірки на допустимість значень внутрішніх змінних класу;
  • гнучкість у правах доступа до внутрішньої змінної: відсутність аксесора set дозволяє реалізувати доступ “тільки для читання” для частини внутрішніх даних класу. Це, в свою чергу, унеможливлює випадкову зміну цих даних.

 

3. Приклад властивості, що реалізує читання/запис змінної типу int

У прикладі демонструється простий випадок доступу до властивості в класі. Оголошується клас з іменем IntNumber в якому реалізовані:

  • внутрішня прихована (private) змінна типу int з іменем d;
  • конструктор з одним параметром, який ініціалізує змінну d;
  • властивість з іменем Number. Ця властивість містить аксесори get та set;
  • метод Display() для виведення значення прихованої змінної d.

Текст класу IntNumber та його використання в функції main() наступний (додаток типу Console Application)

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

namespace ConsoleApp7
{
    // найпростіший приклад класу, в якому використовуються властивості
    class IntNumber
    {
        private int d;

        // конструктор
        public IntNumber(int _d)
        {
            d = _d;
        }

        // оголошення властивості з іменем Number
        private int Number
        {
            // читання поля d
            get
            {
                return d;
            }

            // запис в поле (змінну) d
            set
            {
                d = value;
            }
        }

        // виведення поля d на екран
        public void Display()
        {
            Console.WriteLine("d = {0}", d);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // оголосити екземпляр з іменем number класу IntNumber
            IntNumber number = new IntNumber(5);

            // вивести значення внутрішньої змінної d
            number.Display(); // d = 5

            // змінити значення d через властивість Number.set
            number.Number = 25;
            number.Display(); // d = 25

            // використати властивість Number.get для читання d
            int t;
            t = number.Number; // t = 25
            Console.WriteLine("t = {0}", t);
        }
    }
}

Результат виконання програми

d = 5
d = 25
t = 25
Press any key to continue . . .

 

4. Як впливають специфікатори доступу до елементів класу (private, public) на доступ до властивості?

Щоб використовувати властивість, вона має бути оголошена в розділі public.

Якщо властивість оголосити з специфікатором доступу private, то ця властивість буде недоступна. У результаті при спробі доступу до властивості компілятор видасть повідомлення про помилку

ClassName.PropName is inaccessible due to its protection level

де PropName – ім’я private-властивості, оголошеної у класі ClassName.



 

5. Приклад властивості, що реалізує доступ до внутрішньої змінної, яка є масивом чисел типу double

У прикладі оголошується клас ArrayDouble, що реалізує масив дійсних чисел. У класі оголошуються:

  • внутрішня прихована (private) змінна A, що є масивом чисел типу double;
  • два конструктори класу;
  • індексатор, який повертає елемент масиву за заданим індексом;
  • властивість Sum, яка визначає аксесори get та set.

Текст класу ArrayDouble та його використання в методі main() наступний

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

namespace ConsoleApp7
{
    // масив типу double
    class ArrayDouble
    {
        double[] A; // масив чисел типу double

        // конструктори класу
        public ArrayDouble(int size)
        {
            // виділити пам'ять під масив
            A = new double[size];

            // заповнити масив нулями
            for (int i = 0; i < size; i++)
                A[i] = 0.0;
        }

        public ArrayDouble(double[] _A)
        {
            // виділити пам'ять для масиву
            A = new double[_A.Length];

            // скопіювати один масив в інший
            for (int i = 0; i < _A.Length; i++)
                A[i] = _A[i];
        }

        // індексатор
        public double this[int index]
        {
            get
            {
                if ((index >= 0) && (index < A.Length))
                    return A[index];
                else
                    return 0.0;
            }
        }

        // властивість Sum:
        // get - визначає суму елементів масиву
        // set - розподіляє елементи в масиві рівниими значеннями, сума елементів рівна value
        public double Sum
        {
            get
            {
                // обчислення суми елементів
                double s = 0;
                for (int i = 0; i < A.Length; i++)
                    s += A[i];
                return s;
            }
            set
            {
                // рівномірний розподіл елементів масиву A значенням value
                double v = value/A.Length;
                for (int i = 0; i < A.Length; i++)
                    A[i] = v;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            double[] X = { 1.0, 2.5, 2.0, 4.5, 0.0 }; // масив
            ArrayDouble ad = new ArrayDouble(X); // створити екземпляр класу ArrayDouble

            double sum; // сума елементів
            sum = ad.Sum; // sum =
            Console.WriteLine("sum = {0}", sum);

            // заповнити масив в екземплярі ad значеннями 1.0
            ad.Sum = 1.0 * X.Length;

            // перевірка
            double t;
            t = ad[2]; // t = 1.0
            Console.WriteLine("t = {0}", t);
        }
    }
}

У класі ArrayDouble реалізовано властивість Sum. Ця властивість визначає два аксесори get та set. З метою демонстрації аксесор get реалізує обчислення суми елементів масиву A

...
public double Sum
{
    get
    {
        // обчислення суми елементів
        double s = 0;
        for (int i = 0; i < A.Length; i++)
            s += A[i];
        return s;
    }
    ...
}
...

Так само з допомогою аксесора set в кожен елемент масиву A заноситься значення value/A.length

...
set
{
    // рівномірний розподіл елементів масиву A значенням value
    double v = value/A.Length;
    for (int i = 0; i < A.Length; i++)
        A[i] = v;
}
...

Висновок: в аксесорах get, set можна задавати додаткові операції обчислення над даними класу. Необов’язково повинно повертатись значення деякого поля в класі.

Результат роботи програми

sum = 10
t = 1
Press any key to continue . . .

 

6. Приклад властивості, що реалізує доступ до структурної змінної (struct)

У прикладі демонструється доступ до структури типу Fraction з допомогою властивостей. Властивості оголошуються в класі Complex, що реалізує комплексне число. Клас Complex декларує наступні елементи:

  • внутрішні приховані (private) змінні real, imag. Ці змінні (поля) відповідають дійсній та уявній частині комплексного числа;
  • властивість Real для читання/запису значення поля real;
  • властивість Imag для читання/запису значення поля imag;
  • властивість RealD, що представляюче значення поля real у вигляді дійсного числа типу double. Ця властивість призначена тільки для читання (get), аксесор set відсутній у властивості;
  • властивість ImagD, що представляє значення поля imag у вигляді дійсного числа типу double. Ця властивість призначена тільки для читання (get);
  • властивість Abs, яка обчислює модуль комплексного числа. Ця властивість призначена тільки для читання.

Текст програми для додатку типу Console Application наступний

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

namespace ConsoleApp7
{
  // структура, що реалізує дробове число
  struct Fraction
  {
    public int num; // чисельник
    public int denom; // знаменник
  }

  // клас, що реалізує комплексне число
  class Complex
  {
    private Fraction real; // дійсна частина комплексного числа у вигляді дробу
    private Fraction imag; // уявна частина комплексного числа

    // конструктори
    public Complex(int _real, int _imag)
    {
      real.num = _real;
      real.denom = 1;
      imag.num = _imag;
      imag.denom = 1;
    }

    public Complex(Fraction _real, Fraction _imag)
    {
      real = _real;
      imag = _imag;
    }

    // властивість що відповідає дійсній частині комплексного числа
    public Fraction Real
    {
      get
      {
        return real;
      }
      set
      {
          real = value;
      }
    }

    // властивість, що відповідає уявній частині комплексного числа
    public Fraction Imag
    {
      get
      {
        return imag;
      }
      set
      {
        imag = value;
      }
    }

    // властивість, що повертає дійсну частину комплексного числа
    // у вигляді типу double
    public double RealD
    {
      get
      {
        return (double)real.num / (double)real.denom;
      }
    }

    // властивість, що повертає уявну частину комплексного числа
    // у вигляді типу double
    public double ImagD
    {
      get
      {
        return (double)imag.num / (double)imag.denom;
      }
    }

    // властивість, що повертає модуль комплексного числа
    public double Abs
    {
      get
      {
        double t,r,i;
        r = real.num / (double)real.denom;
        i = imag.num / (double)imag.denom;
        t = Math.Sqrt(r * r + i * i);
        return t;
      }
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      Fraction r = new Fraction();
      Fraction i = new Fraction();
      r.num = 3;
      r.denom = 1;
      i.num = 4;
      i.denom = 1;

      Complex cm = new Complex(r, i); // створити екземпляр класу Complex

      // демонстрація роботи властивостей класу Complex
      // властивість Abs
      double abs = cm.Abs; // взяти модуль, abs = 5
      Console.WriteLine("Abs = {0}", abs);

      // властивості RealD, RealI
      double rd, id;
      rd = cm.RealD; // rd = 3/1 = 3.0
      id = cm.ImagD; // id = 4/1 = 4.0
      Console.WriteLine("rd = {0}", rd);

      // властивість Real
      Fraction R = new Fraction();
      R = cm.Real; // R = { 3, 1 }
      Console.Write("R.num = {0},   ", R.num);
      Console.WriteLine("R.denom = {0}", R.denom);
    }
  }
}

Результат роботи програми

Abs = 5
rd = 3
R.num = 3,   R.denom = 1
Press any key to continue . . .

 

7. Приклад властивості, що реалізує доступ до об’єкту класу

У прикладі демонструється доступ до об’єкту класу з допомогою властивості.

Оголошується клас Point, що реалізує точку на координатній площині. У класі оголошуються:

  • дві внутрішні приховані (private) змінні x, y що є координатами точки;
  • два конструктори;
  • властивості X, Y. Ці властивості з допомогою аксесорів get, set реалізують доступ до прихованих до змінних x, y.

Також оголошується клас Line, що реалізує лінію яка складається з двох точок типу Point. У класі реалізовано:

  • приховані (private) внутрішні змінні p1, p2 типу Point. Це є точки кінців відрізка;
  • властивості P1, P2 що реалізують доступ до внутрішніх змінних p1, p2. У цих властивостях продемонстровано повернення об’єкту класу типу Point;
  • властивість Length, що визначає довжину лінії. Ця властивість містить тільки один аксесор get. Аксесор set відсутній.

Текст демонстраційної програми, створеної за шаблоном Console Application наступний

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

namespace ConsoleApp7
{
  // клас, що реалізує точку
  class Point
  {
    private float x;
    private float y;

    // конструктори
    public Point()
    {
      x = y = 0f;
    }

    public Point(float _x, float _y)
    {
      x = _x;
      y = _y;
    }

    // властивості
    public float X
    {
      get { return x; }
      set { x = value; }
    }

    public float Y
    {
      get { return y; }
      set { y = value; }
    }
  }

  // клас, що реалізує лінію
  class Line
  {
    // внутрішні дані - точки лінії, об'єкти класу Point
    private Point p1;
    private Point p2;

    // конструктор
    public Line(Point _p1, Point _p2)
    {
      p1 = new Point();
      p1.X = _p1.X;
      p1.Y = _p1.Y;

      p2 = new Point(_p2.X, _p2.Y);
    }

    // властивості
    public Point P1 // отримати першу точку лінії
    {
      get { return p1; }
      set { p1 = value; }
    }

    public Point P2 // отримати другу точку лінії
    {
      get { return p2; }
      set { p2 = value; }
    }

    // властивість, що визначає довжину лінії,
    // містить тільки один аксесор get
    public double Length
    {
      get
      {
        double len;
        len = Math.Sqrt(p1.X * p1.X + p2.Y * p2.Y);
        return len;
      }
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // доступ до об'єкту класу з допомогою властивості
      // створити екземпляри класів
      Point p1 = new Point(2, 4);
      Point p2 = new Point(6, 8);
      Line line = new Line(p1, p2);

      // демонстрація властивостей P1, P2 класу Line
      Point p3, p4;
      p3 = line.P1; // аксесор get
      p4 = line.P2;
      float x, y;
      x = p3.X; // x = 2
      y = p3.Y; // y = 4
      Console.WriteLine("x = {0},   y = {1}", x, y);

      p3.X = -3;
      p3.Y = -1;
      line.P1 = p3; // аксесор set
      Console.WriteLine("x = {0},   y = {1}", line.P1.X, line.P1.Y);

      // вивести довжину лінії з допомогою властивості Length
      double length;
      length = line.Length;
      Console.WriteLine("Length = {0}", length);
    }
  }
}

Як видно з вищенаведеного коду, у класі Line реалізовано дві властивості, які здійснюють доступ до об’єкту класу Point

...
// властивості
public Point P1 // отримати першу точку лінії
{
  get { return p1; }
  set { p1 = value; }
}

public Point P2 // отримати другу точку лінії
{
  get { return p2; }
  set { p2 = value; }
}
...

Результат виконання програми

x = 2, y = 4
x = -3, y = -1
Length = 8.54400374531753
Press any key to continue . . .

 

8. Чи визначають властивості місце в пам’яті для полів класу?

Ні, не визначають. Властивості керують доступом до полів. Сама властивість не представляє поле (член даних) класу. Поле повинно бути визначене в класі незалежно від властивості.

 


Зв’язані теми