- •Классы и структуры
- •Функции-члены
- •Передача параметров в методы
- •Параметры ref
- •Параметры out
- •Свойства
- •Свойства, доступные только для чтения и только для записи
- •Модификаторы доступа для свойств
- •Автоматически реализуемые свойства
- •Замечание о встраивании
- •Индексаторы
- •Тип_элемента this[int индекс]
- •Листинг 1.
- •Листинг 2.
- •Листинг 3.
- •Листинг 4.
- •Конструкторы
- •Статические конструкторы
- •Вызов одних конструкторов из других
- •Поля readonly
- •Анонимные типы
- •Структуры
- •Структуры это типы значений
- •Структуры и наследование
- •Конструкторы структур
- •Частичные классы
- •Статические классы
- •Класс Object
- •Методы System.Object
- •Метод ToString()
- •Расширяющие методы
Автоматически реализуемые свойства
Если наличие какой-либо логики в методах set и get свойства не предполагается, можно использовать автоматически реализуемые свойства. Такие свойства создают поддерживающие их переменные-члены автоматически. Код ранее приведенного примера с Age выглядел бы следующим образом:
public int Age (get; set;}
В объявлении private int age; нет необходимости — компилятор создаст его автоматически.
При использовании автоматически реализуемых свойств проверка достоверности свойства не может быть выполнена в его методе set. Поэтому в предыдущем примере мы бы не смогли проверить корректность установки возраста. Также должны присутствовать оба метода доступа. То есть, попытка сделать свойство доступным только для чтения вызовет ошибку:
public int Age (get;)
Однако уровень доступа каждого метода доступа может быть различным. Поэтому следующий код является допустимым:
public int Age (get; private set; }
Замечание о встраивании
Некоторые разработчики могут быть обеспокоены тем, что в предыдущих разделах демонстрируется множество ситуаций, когда стандартная практика кодирования С# порождает множество очень маленьких функций например, для доступа к полю через свойство вместо непосредственного обращения к нему. Не повлияет ли это на производительность из-за накладных расходов, связанных с дополнительными вызовами функций? Ответ состоит в том, что не нужно беспокоиться о потере производительности по причине такой методологии программирования на С#. Вспомните, что код С# транслируется в IL, а затем JIT компилирует его во время выполнения в родной исполняемый код. JIT-компилятор спроектирован так, что генерирует высоко оптимизированный код и без колебаний использует встроенный (inline) код там, где это необходимо (т.е. заменяет вызовы функций встроенным кодом). Метод или свойство, чья реализация просто вызывает другой метод или возвращает поле, почти наверняка будет преобразован во встроенный код. Однако следует отметить, что решение относительно встраивания принимает исключительно CLR. У вас нет никакой возможности управлять тем, какие методы будут встроенными например, с помощью какого-то ключевого слова вроде применяемого в С++ inline.
Индексаторы
Как вам должно быть уже известно, индексирование массива осуществляется с помощью оператора []. Для создаваемых классов можно определить оператор [], но с этой целью вместо операторного метода создается индексатор, который позволяет индексировать объект, подобно массиву. Индексаторы применяются, главным образом, в качестве средства, поддерживающего создание специализированных массивов, на которые накладывается одно или несколько ограничений. Тем не менее индексаторы могут служить практически любым целям, для которых выгодным оказывается такой же синтаксис, как и у массивов. Индексаторы могут быть одно- или многомерными.
Рассмотрим сначала одномерные индексаторы.
Создание одномерных индексаторов
Ниже приведена общая форма одномерного индексатора:
Тип_элемента this[int индекс]
{
// Аксессор для получения данных
get
{
// Возврат значения, которое определяет индекс.
}
// Аксессор для установки данных
set
{
// Установка значения, которое определяет индекс.
}
}
где тип_элемента обозначает конкретный тип элемента индексатора. Следовательно, у каждого элемента, доступного с помощью индексатора, должен быть определенный тип_элемента. Этот тип соответствует типу элемента массива. Параметр индекс получает конкретный индекс элемента, к которому осуществляется доступ. Формально этот параметр совсем не обязательно должен иметь тип int, но поскольку индексаторы, как правило, применяются для индексирования массивов, то чаще всего используется целочисленный тип данного параметра.
В теле индексатора определены два аксессора (т.е. средства доступа к данным): get и set. Аксессор подобен методу, за исключением того, что в нем не объявляется тип возвращаемого значения или параметры. Аксессоры вызываются автоматически при использовании индексатора, и оба получают: индекс в качестве параметра. Так, если индексатор указывается в левой части оператора присваивания, то вызывается аксессор set и устанавливается элемент, на который указывает параметр индекс. В противном случае вызывается аксессор get и возвращается значение, соответствующее параметру индекс. Кроме того, аксессор set получает неявный параметр value, содержащий значение, присваиваемое по указанному индексу.
Преимущество индексатора заключается, в частности, в том, что он позволяет полностью управлять доступом к массиву, избегая нежелательного доступа. В качестве примера рассмотрим программу, в которой создается класс FailSoftArray, реализующий массив для выявления ошибок нарушения границ массива, а следовательно, для предотвращения исключительных ситуаций, возникающих во время выполнения в связи с индексированием массива за его границами. Для этого массив инкапсулируется в качестве закрытого члена класса, а доступ к нему осуществляется только с помощью индексатора. При таком подходе исключается любая попытка получить доступ к массиву за его границами, причем эта попытка пресекается без катастрофических последствий для программы. А поскольку в классе FailSoftArray используется индексатор, то к массиву можно обращаться с помощью обычной формы записи.