Классы, структуры и пространства имен
C# является полноценным объектно-ориентированным языком. Это значит, что программу на C# можно представить в виде взаимосвязанных взаимодействующих между собой объектов.
Описанием объекта является класс , а объект представляет экземпляр этого класса. Можно еще провести следующую аналогию. У нас у всех есть некоторое представление о человеке, у которого есть имя, возраст, какие-то другие характеристики. То есть некоторый шаблон — этот шаблон можно назвать классом. Конкретное воплощение этого шаблона может отличаться, например, одни люди имеют одно имя, другие — другое имя. И реально существующий человек (фактически экземпляр данного класса) будет представлять объект этого класса.
В принципе ранее уже использовались классы. Например, тип string , который представляет строку, фактически является классом. Или, например, класс Console , у которого метод WriteLine() выводит на консоль некоторую информацию. Теперь же посмотрим, как мы можем определять свои собственные классы.
По сути класс представляет новый тип, который определяется пользователем. Класс определяется с помощью ключевого слова сlass :
После слова class идет имя класса и далее в фигурных скобках идет собственно содержимое класса. Например, определим в файле Program.cs класс Person, который будет представлять человека:
Однако такой класс не особо показателен, поэтому добавим в него некоторую функциональность.
Поля и методы класса
Класс может хранить некоторые данные. Для хранения данных в классе применяются поля . По сути поля класса — это переменные, определенные на уровне класса.
Кроме того, класс может определять некоторое поведение или выполняемые действия. Для определения поведения в классе применяются методы.
Итак, добавим в класс Person поля и методы:
В данном случае в классе Person определено поле name , которое хранит имя, и поле age , которое хранит возраст человека. В отличие от переменных, определенных в методах, поля класса могут иметь модификаторы, которые указываются перед полем. Так, в данном случае, чтобы все поля были доступны вне класса Person поля определены с модификатором public .
При определении полей мы можем присвоить им некоторые значения, как в примере выше в случае переменной name . Если поля класса не инициализированы, то они получают значения по умолчанию. Для переменных числовых типов это число 0.
Также в классе Person определен метод Print() . Методы класса имеют доступ к его поля, и в данном случае обращаемся к полям класса name и age для вывода их значения на консоль. И чтобы этот метод был виден вне класса, он также определен с модификатором public .
Создание объекта класса
После определения класса мы можем создавать его объекты. Для создания объекта применяются конструкторы . По сути конструкторы представляют специальные методы, которые называются так же как и класс, и которые вызываются при создании нового объекта класса и выполняют инициализацию объекта. Общий синтаксис вызова конструктора:
new конструктор_класса(параметры_конструктора);
Сначала идет оператор new , который выделяет память для объекта, а после него идет вызов конструктора .
Конструктор по умолчанию
Если в классе не определено ни одного конструктора (как в случае с нашим классом Person), то для этого класса автоматически создается пустой конструктор по умолчанию, который не принимает никаких параметров.
Теперь создадим объект класса Person:
Person tom = new Person(); // создание объекта класса Person // определение класса Person class Person < public string name = "Undefined"; public int age; public void Print() < Console.WriteLine($"Имя: Возраст: "); > >
Для создания объекта Person используется выражение new Person() . В итоге после выполнения данного выражения в памяти будет выделен участок, где будут храниться все данные объекта Person. А переменная tom получит ссылку на созданный объект, и через эту переменную мы можем использовать данный объект и обращаться к его функциональности.
Обращение к функциональности класса
Для обращения к функциональности класса — полям, методам (а также другим элементам класса) применяется точечная нотация точки — после объекта класса ставится точка, а затем элемент класса:
объект.поле_класса объект.метод_класса(параметры_метода)
Например, обратимся к полям и методам объекта Person:
Person tom = new Person(); // создание объекта класса Person // Получаем значение полей в переменные string personName = tom.name; int personAge = tom.age; Console.WriteLine($"Имя: Возраст "); // Имя: Undefined Возраст: 0 // устанавливаем новые значения полей tom.name = "Tom"; tom.age = 37; // обращаемся к методу Print tom.Print(); // Имя: Tom Возраст: 37 class Person < public string name = "Undefined"; public int age; public void Print() < Console.WriteLine($"Имя: Возраст: "); > >
Консольный вывод данной программы:
Имя: Undefined Возраст: 0 Имя: Tom Возраст: 37
Добавление класса
Обычно классы помещаются в отдельные файлы. Нередко для одного класса предназначен один файл. Если мы работаем над проектом вне среды Visual Studio, используя .NET CLI, то нам достаточно добавить новый файл класса в папку проекта. Например, добавим новый файл, который назовем Person.cs и в котором определим следующий код:
Здесь определен класс Person с одним полем name и методом Print.
В файле Program.cs , который представляет основной файл программы используем класс Person:
Person tom = new Person(); tom.name = "Tom"; tom.Print(); // Person Tom
Visual Studio предоставляет по умолчанию встроенные шаблоны для добвления класса. Для добавления класса нажмем в Visual Studio правой кнопкой мыши на название проекта:
В появившемся контекстном меню выберем пункт Add -> New Item. (или Add -> Class. )
В открывшемся окне добавления нового элемента убедимся, что в центральной части с шаблонами элементов у нас выбран пункт Class . А внизу окна в поле Name введем название добавляемого класса — пусть он будет назваться Person :
В качестве названия класса можно вводить как Person, так и Person.cs. И после нажатия на кнопку добавления в проект будет добавлен новый класс, в котором можно определить тот же код и также использовать в файле Program.cs.
Таким образом, мы можем определять классы в отдельных файлах и использовать их в программе.
Общие сведения о классах
Тип, определенный как , class является ссылочным типом. Во время выполнения при объявлении переменной ссылочного типа переменная содержит значение null , пока вы явно не создадите экземпляр класса с помощью new оператора или не назначите ему объект совместимого типа, который может быть создан в другом месте, как показано в следующем примере:
//Declaring an object of type MyClass. MyClass mc = new MyClass(); //Declaring another object of the same type, assigning it the value of the first object. MyClass mc2 = mc;
При создании объекта выделяется достаточный объем памяти для этого объекта в управляемой куче, и переменная хранит только ссылку на расположение данного объекта. Память, используемая объектом, освобождается с помощью функции автоматического управления памятью среды CLR, которая называется сборкой мусора. Дополнительные сведения о сборке мусора см. в разделе Автоматическое управление памятью и сборка мусора.
Объявление классов
Классы объявляются с помощью class ключевое слово за которым следует уникальный идентификатор, как показано в следующем примере:
//[access modifier] - [class] - [identifier] public class Customer < // Fields, properties, methods and events go here. >
Дополнительный модификатор class доступа предшествует ключевое слово. Так как public используется в этом случае, любой пользователь может создавать экземпляры этого класса. За именем класса следует ключевое слово class . Имя класса должно быть допустимым именем идентификатора C#. Оставшаяся часть определения — это тело класса, в котором задаются данные и поведение. Поля, свойства, методы и события в классе собирательно называются членами класса.
Создание объектов
Хотя они иногда используются взаимозаменяемо, класс и объект — это разные вещи. Класс определяет тип объекта, но сам по себе он не является объектом. Объект — это конкретная сущность, основанная на классе, которую иногда называют экземпляром класса.
Объекты можно создать с помощью new ключевое слово за которым следует имя класса, как показано ниже:
Customer object1 = new Customer();
При создании экземпляра класса ссылка на объект передается программисту. В предыдущем примере object1 представляет собой ссылку на объект, который основан на Customer . Эта ссылка ссылается на новый объект , но не содержит сами данные объекта. Фактически, можно создать ссылку на объект без создания собственно объекта:
Не рекомендуется создавать ссылки на объекты, которые не ссылаются на объект, так как попытка получить доступ к объекту через такую ссылку завершается сбоем во время выполнения. Ссылку можно сделать для ссылки на объект, создав новый объект или назначив ему существующий объект, например:
Customer object3 = new Customer(); Customer object4 = object3;
В этом коде создаются две ссылки на объект, которые указывают на один и тот же объект. Таким образом, любые изменения объекта, выполненные посредством object3 , отражаются при последующем использовании object4 . Поскольку на объекты, основанные на классах, указывают ссылки, классы называют ссылочными типами.
Конструкторы и инициализация
В предыдущих разделах представлен синтаксис для объявления типа класса и создания экземпляра этого типа. При создании экземпляра типа необходимо убедиться, что его поля и свойства инициализированы полезными значениями. Существует несколько способов инициализации значений.
- Принятие значений по умолчанию
- Инициализаторы полей
- Параметры конструктора
- Инициализаторы объектов
Каждый тип .NET имеет значение по умолчанию. Как правило, это значение равно 0 для числовых типов и null для всех ссылочных типов. Вы можете использовать это значение по умолчанию, если это целесообразно в приложении.
Если значение по умолчанию .NET не является правильным, можно задать начальное значение с помощью инициализатора поля:
Вы можете потребовать, чтобы вызывающие стороны предоставляли начальное значение, определив конструктор , который отвечает за установку этого начального значения:
public class Container < private int _capacity; public Container(int capacity) =>_capacity = capacity; >
Начиная с C# 12, можно определить основной конструктор как часть объявления класса:
public class Container(int capacity)
Добавление параметров в имя класса определяет основной конструктор. Эти параметры доступны в теле класса, который включает его члены. Их можно использовать для инициализации полей или в любом другом месте, где они необходимы.
Вы также можете использовать модификатор required для свойства и разрешить вызывающим объектам использовать инициализатор объекта для задания начального значения свойства:
public class Person < public required string LastName < get; set; >public required string FirstName < get; set; >>
Добавление required ключевое слово требует, чтобы вызывающие объекты должны задавать эти свойства как часть new выражения:
var p1 = new Person(); // Error! Required properties not set var p2 = new Person() < FirstName = "Grace", LastName = "Hopper" >;
Наследование классов
Классы полностью поддерживают наследование, фундаментальный механизм объектно ориентированного программирования. При создании класса можно наследовать от любого другого класса, который не определен как sealed . Другие классы могут наследовать от класса и переопределять виртуальные методы класса. Кроме того, можно реализовать один или несколько интерфейсов.
При наследовании создается производный класс, то есть класс объявляется с помощью базового класса, от которого он наследует данные и поведение. Базовый класс задается добавлением после имени производного класса двоеточия и имени базового класса, как показано далее:
public class Manager : Employee < // Employee fields, properties, methods and events are inherited // New Manager fields, properties, methods and events go here. >
Когда объявление класса включает базовый класс, оно наследует все члены базового класса, кроме конструкторов. Дополнительные сведения см. в разделе Наследование.
Класс в C# может напрямую наследовать только от одного базового класса. Однако поскольку базовый класс может сам наследовать от другого класса, класс может косвенно наследовать несколько базовых классов. Кроме того, класс может напрямую реализовать несколько интерфейсов. Дополнительные сведения см. в разделе Интерфейсы.
Класс можно объявить как abstract . Абстрактный класс содержит абстрактные методы, которые имеют определение сигнатуры, но не имеют реализации. Не удается создать экземпляр абстрактных классов. Они могут использоваться только через производные классы, реализующие абстрактные методы. В отличие от этого, запечатанный класс не позволяет другим классам наследоваться от него. Дополнительные сведения см. в статье Абстрактные и запечатанные классы и члены классов.
Определения классов можно разделить между различными исходными файлами. Дополнительные сведения см. в разделе Разделяемые классы и методы.
Спецификация языка C#
Дополнительные сведения см. в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.