Классы и объекты C#: свойства

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

На данный момент мы познакомились с таким типом данных в C# как класс, научились создавать объекты и использовать инициализаторы объектов. Сегодня мы познакомимся с таким понятием как свойство и рассмотрим основные модификаторы доступа к членам класса.

Введение

На данный момент у нас есть класс, описывающие некоторое здание прямоугольной формы:

class Building
{
    public double width;
    public double length;
    public double height;

    //Конструктор
    public Building(double width, double length, double height = 3)
    {
        this.width = width;
        this.length = length;
        this.height = height;
    }

    public Building(): this(20, 20, 3)
    { 
    
    }

    //метод
    public double GetVolume()
    {
        return width * length * height;
    }
}

У этого класса определено три поля (длина, ширина, высота), два конструктора и  один метод, возвращающий объем здания. С точки зрения синтаксиса языка C# с этим классом никаких проблем нет — компилятор не видит ошибок, а программа, использующая этот класс, прекрасно работает. Однако у этого класса поля (читай — переменные) определены с модификатором public, что считается плохим тоном в программировании. Все переменные классы должны быть недоступны извне, а доступ к ним должен осуществляться через свойства.  Но, прежде, чем мы перейдем к объявлению свойств, рассмотрим какие модификаторы доступа к членам класса могут использоваться в C#.

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

Всего в C# существует четыре ключевых слова для указания уровня доступа к классам и членам класса: public, private, protected и internal. Из этих ключевых слов определяются шесть уровней доступа:

  1. public — доступ к типу или члену класса возможен из любого другого кода в том числе из извне.
  2. private — доступ к типу или члену возможен только из кода в том же объекте class или struct.
  3. protected — доступ к типу или члену возможен только из кода в том же объекте class либо в class, производном от этого class.
  4. internal — доступ к типу или члену возможен из любого кода в той же сборке, но не из другой сборки.
  5. protected internal — доступ к типу или члену возможен из любого кода в той сборке, где он был объявлен, или из производного class в другой сборке.
  6. private protected — доступ к типу или члену возможен только из его объявляющей сборки из кода в том же class либо в типе, производном от этого class. Этот уровень доступа можно использовать, начиная с версии C# 7.2.

Модификаторы доступа можно указывать явно, например, как в нашем классе:

public double width; 
public double length; 
public double height;

а можем не указывать, тогда к члену класса по умолчанию будет применен модификатор private. Классы (class) и структуры (struct), которые объявляются без модификатора, по умолчанию имеют доступ internal. Также,  все классы и структуры, определенные в пространствах имен и не являющиеся вложенными в другие классы, могут иметь только два модификатора доступа — public или internal. Примеры использования различных модификаторов доступа рассмотрим на примере нашего класса, но, прежде, чем мы это сделаем, стоит разобраться с тем, что такое свойство.

Свойства

Как было сказано выше — давать доступ к полям класса извне (объявлять их с модификатором доступа public) — признак плохого программирования. И в прошлой части, касающейся работы с классами, модификатор public был использован всего лишь с одной целью — чтобы максимально кратко объяснить суть работы инициализаторов объектов без углубления в тему свойств класса.

Почему не стоит давать прямой доступ к полям класса? Это можно, опять же продемонстрировать на таком простом примере как наш Building: сейчас мы открыли доступ к полям width, length и height извне. При этом, все три поля имеют тип double. Таким образом, зная,что из себя представляет этот тип данных, мы можем без проблем сделать вот так:

Building building = new Building();
building.height = -100.5;

никакой ошибки нет — мы просто задали отрицательную высоту здания и получим в итоге отрицательный объем. Так вот,в том числе, чтобы избежать подобных ситуаций и могут применяться свойства. Свойство позволяет обеспечить контролируемый доступ к полям класса — обеспечить дополнительную логику. Например,мы можем сделать ограничения по максимальному и минимальному значению поля, можем запретить изменять какое-либо поле класса и так далее.

В общем случае, свойство определяется в классе следующим образом:

[модификатор_доступа] тип имя_свойства 
{
  get {возвращаемое_значение;}
  set {устанавливаемое_значение;}
}

Здесь модификатор_доступа — это один из шести модификаторов доступа, рассмотренных выше. В большинстве ситуаций используется модификатор public. Далее идёт  тип_данных и имя свойства.  В фигурных скобках содержатся блоки get — для чтения поля,которому соответствует свойство и set — для записи поля.  Например, создадим свойство, с помощью которого мы будем определять высоту здания (представлена только часть кода, демонстрирующая объявление свойства):

class Building
{
    double height;

    public double Height
    {
        get { return height; }
        set
        {
            if (value < 0)
                throw new Exception("Высота здания не может быть менее 0 метров");
            else
                height = value;
        }
    }
}

На что здесь стоит обратить внимание:

  1. поле height у нас теперь без модификатора доступа (по умолчанию оно стало private)
  2. объявлено публичное свойство Height, причем в блоке set (запись поля) реализована дополнительная логика — проверка того, что задается положительное значение высоты здания.
В нашем примере поле height и свойство Height различаются только написанием первой буквы. В C# принято следующее правило: поля класса пишутся с маленькой буквы, а названия свойств и методов — с большой. В принципе, никто не запрещает назвать поля и свойства по-разному — код будет работать, но считается хорошим тоном именовать поля и свойства одинаковыми словами (желательно, существительными)

Теперь, если попробовать запустить приложение и выполнить вот такое присваивание:

Building building = new Building();
building.Height = -100.5;

то программа выдаст ошибку:

System.Exception HResult=0x80131500 Сообщение = Высота здания не может быть менее 0 метров Источник = FirstClass

Так же, мы можем объявлять свойства доступные только для чтения (у таких свойств будет отсутствовать блок set) и только для записи (у таких свойств отсутствует блок get). Второй вариант свойств (только для записи) встречается крайне редко, а вот свойства только для чтения — довольно часто. Например, мы можем создать свойство «Объем» следующим образом:

class Building
{
    //метод
    private double GetVolume()
    {
        return width * length * height;
    }

    //Свойства
    public double Volume { get { return GetVolume(); } }
}

здесь мы объявили свойство Volume доступное только для чтения. При этом, методу GetVolume() мы установили модификатор private, а свойству — public.

Теперь, познакомившись с тем, что такое свойства в C# перепишем допишем наш класс и создадим необходимые свойства для задания параметров:

class Building
{
    private double width;
    private double length;
    private double height;

    public Building() 
    {
        width = 10;
        length = 10;
        height = 2;
    }

    //метод
    private double GetVolume()
    {
        return width * length * height;
    }

    //Свойства
    public double Height
    {
        get { return height; }
        set
        {
            if (value < 0)
                throw new Exception("Высота здания не может быть менее 0 метров");
            else
                height = value;
        }
    }

    public double Width
    {
        get { return width; }
        set
        {
            if (value < 0)
                throw new Exception("Ширина здания не может быть менее 0 метров");
            else
                width = value;
        }
    }

    public double Length
    {
        get { return length; }
        set
        {
            if (value < 0)
                throw new Exception("Длина здания не может быть менее 0 метров");
            else
                length = value;
        }
    }


    public double Volume { get { return GetVolume(); } }
}

Чтобы обратиться к свойству мы должны написать имя объекта, нажать на клавиатуре точку и выбрать интересующее нас свойство:

Сокращенная форма записи свойств в C#

Если в блоках get и set свойства не реализуется никакая дополнительная логика, то допускается сокращенная записи свойства. Например, добавим в наш объект свойство Name (название здания) и применим сокращенную форму записи:

class Building
{
    public string Name { get; set; }
}

Как видите, в этом случае нам не требуется объявлять в классе дополнительное поле, а блоки get и set остаются пустыми. Такие свойства также называются автосвойствами.

Выражения для свойств в C#

Если реализация свойства представляет собой одиночное выражение, в качестве метода получения или задания можно использовать выражения для свойств. Например, у нас в классе у свойств Width, Length, Height и Volume в блоке get используется одиночное выражение, более, того, в логическую операцию в блоке set мы также можем «свернуть», используя тернарную операцию, поэтому мы можем упростить код нашего класса, используя выражения C#:

class Building
{
    private double width;
    private double length;
    private double height;

    public Building()
    {
        width = 10;
        length = 10;
        height = 2;
    }

    //метод
    private double GetVolume()
    {
        return width * length * height;
    }

    //Свойства
    public double Height
    {
        get => height;
        set => height = value > 0 ? value : throw new Exception("Высота здания не может быть менее 0 метров");
    }

    public double Width
    {
        get => width;
        set => width = value > 0 ? value : throw new Exception("Ширина здания не может быть менее 0 метров");
    }

    public double Length
    {
        get => length;
        set => length = value > 0 ? value : throw new Exception("Длина здания не может быть менее 0 метров");
    }

    public double Volume => GetVolume();
}

Использование выражений свойств, также, как и выражение switch о котором мы говорили, когда рассматривали логические операции C#, позволяют сделать код короче и интуитивно понятнее.

Значение свойства по умолчанию в C#

Иногда бывает необходимо инициализировать значение свойства значением, отличным, от значения по умолчанию для типа данных. Например, наше новое свойство Name имеет тип данных string (строка) и по умолчанию будет инициализировано как null, так как строки относятся к ссылочным типам данных. Если мы хотим, чтобы сразу при создании объекта наше свойство получило некоторое значение по умолчанию, то мы можем сделать это следующим образом:

class Building
{
    public string Name { get; set; } = "Неизвестное здание";
}

То есть сразу после закрывающей скобки } для свойства ставится равно и пишется значение свойства по умолчанию.

Итого

Итак, сегодня мы познакомились в общих чертах с модификаторами и уровнями доступа к членам классов (полям, свойствам и методам), а также разобрались с тем, что представляют из себя свойства, как их объявлять в классе, делать свойства C# доступными только для чтения, а также использовать автосвойства и выражения для свойств C#. В следующий раз мы продолжим разбираться с классами и объектами C# и рассмотрим такой вопрос, как наследование.

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
guest
0 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии