- •Классы и структуры
- •Функции-члены
- •Передача параметров в методы
- •Параметры ref
- •Параметры out
- •Свойства
- •Свойства, доступные только для чтения и только для записи
- •Модификаторы доступа для свойств
- •Автоматически реализуемые свойства
- •Замечание о встраивании
- •Индексаторы
- •Тип_элемента this[int индекс]
- •Листинг 1.
- •Листинг 2.
- •Листинг 3.
- •Листинг 4.
- •Конструкторы
- •Статические конструкторы
- •Вызов одних конструкторов из других
- •Поля readonly
- •Анонимные типы
- •Структуры
- •Структуры это типы значений
- •Структуры и наследование
- •Конструкторы структур
- •Частичные классы
- •Статические классы
- •Класс Object
- •Методы System.Object
- •Метод ToString()
- •Расширяющие методы
Анонимные типы
В разделе 6.2 обсуждалось ключевое слово var, ссылающееся на неявно типизированные переменные. При использовании его вместе с ключевым словом new можно создавать анонимные типы. Анонимный тип это просто безымянный класс, унаследованный от Object. Определение класса выводится компилятором из его инициализатора, как и в случае с неявно типизированными переменными.
Если нужен объект, содержащий имя, отчество и фамилию лица, то его объявление может выглядеть так:
var captain = new {FirstName = "James", MiddleName = "T",
LastName = "Kirk"};
Это создаст объект со свойствами FirstName, MiddleName и LastName. И если вы создадите другой объект, который выглядит следующим образом:
var doctor = new {FirstName = "Leonard", MiddleName = "",
LastName = "McCoy"};
то типы captain и doctor будут одинаковыми. Так, например, можно установить captain = doctor.
Если устанавливаемое в объект значение поступает от другого объекта, то инициализатор может быть сокращен. Если уже имеется класс, содержащий свойства FirstName, MiddleName и LastName, и есть экземпляр этого класса с именем экземпляра person, то объект captain может быть инициализирован, как показано ниже:
var captain = new (person.FirstName, person.MidleName,
person.LastName};
Имена свойств из объекта person будут спроецированы на новый объект по имени captain. Таким образом, объект по имени captain будет иметь свойства FirstName, MiddleName и LastName.
Действительное имя типа этих объектов неизвестно. Компилятор создает имя этого типа, и только компилятор может его использовать. Поэтому вы не можете и не должны пытаться использовать какую-либо рефлексию типа нового объекта, потому что не получите согласованного результата.
Структуры
До сих пор вы видели, насколько хорошо классы позволяют обеспечить инкапсуляцию объектов в программах. Также было показано, как они сохраняются в куче, обеспечивая гибкость в отношении времени жизни данных, однако с некоторыми затратами производительности. Эти затраты производительности лишь небольшая плата за оптимизацию управляемой кучи. Однако в некоторых ситуациях все, что нужно это маленькая структура данных. В этом случае класс предоставляет больше функциональности, чем требуется, и из соображений производительности имеет смысл отдать предпочтение структуре. Рассмотрим пример:
class Dimensions
{
public double Length;
public double Width;
}
Этот код определяет класс Dimensions, который просто сохраняет длину и ширину элемента. Возможно, вы пишете программу для расстановки мебели, которая позволит людям экспериментировать на компьютере с разными вариантами расположения мебели, и требуется хранить размеры каждой единицы мебели. На первый взгляд, кажется, что здесь нарушены правила хорошего программного дизайна тем, что поля общедоступны, но дело в том, что в данном случае вовсе не нужны все возможности, предлагаемые/классами. Все, что необходимо два числа, которые удобнее хранить вместе, чем по отдельности. Нет необходимости во множестве методов, не требуется наследование от этого класса, и не нужны никакие возможные проблемы для .NET, связанные с обслуживанием кучи — с соответствующим влиянием на производительность для того, чтобы просто сохранить два числа.
Как упоминалось ранее в этой главе, единственное, что следует изменить в коде, чтобы сделать этот тип структурой вместо класса просто заменить ключевое слово class на struct:
struct Dimensions
{
public double Length;
public double Width;
}
Определение функций для структур выглядит точно так же, как определение функций для классов. В следующем коде показана структура с конструктором и свойством:
struct Dimensions
{
public double Length;
public double Width;
Dimensions(double length, double width)
{
Length=length;
Width=width;
}
public int Diagonal
{
get
{
return Math.Sqrt(Length*Length + Width*Width);
}
}
}
Структуры это типы значений, а не ссылочные типы. Это значит, что они либо сохраняются в стеке, либо являются встроенными (последнее если они являются частью другого объекта, хранимого в куче), и имеют те же ограничения времени жизни, что и простые типы данных.
-
Структуры не поддерживают наследование.
-
Имеется некоторое отличие в работе конструкторов структур. В частности, компилятор всегда генерирует конструктор по умолчанию без параметров, который переопределить невозможно.
-
Для структур можно указывать то, как поля размещаются в памяти (это рассматривается в разделе 6.14, посвященной, помимо прочего, атрибутам).
Поскольку структуры на самом деле предназначены для группирования данных, в большинстве случаев все их поля объявляются общедоступными. Строго говоря, это противоречит рекомендациям по написанию кода .NET. Согласно Microsoft, поля (кроме константных) всегда должны быть приватными и помещенными в оболочки общедоступных свойств. Однако что касается простых структур, многие разработчики считают применение общедоступных полей допустимой практикой программирования.
В следующих разделах некоторые отличия между классами и структурами рассматриваются более подробно.