Классы и объекты

Класс – основной тип данных языка 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# конструктор 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. Используется для локальных переменных и констант.

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

Смотрите также: