На этом шаге мы рассмотрим назначение и использование этой инструкции.
Представим себе такую ситуацию. В программе создан объект класса, и у этого объекта есть поле (например, целочисленное). Мы не можем создать указатель для объекта, но можем создать указатель для поля, поскольку фактически это некоторая переменная. Но при этом мы сталкиваемся с потенциальной проблемой. Дело в том, что если в какой-то момент окажется, что в программе ссылок на объект больше нет (а это вполне реальная ситуация), то объект из памяти может быть удален. И тогда указатель будет ссылаться на область памяти, которая больше не используется для хранения значения поля. Такие ситуации считаются (и являются) потенциально опасными. Они блокируются: просто так указатель на поле объекта не создать, приходится использовать "специальный подход".
Для предотвращения удаления (системой сборки мусора) переменных (например, поля объекта), на которые ссылаются указатели, используется инструкция fixed. Шаблон использования этой инструкции представлен ниже:
fixed(тип* указатель = &переменная){ // Команды }
После ключевого слова fixed в круглых скобках объявляется указатель, которому присваивается значение (адрес переменной или поля объекта). Команды, выполняемые с привлечением данного указателя, размещаются в отдельном блоке, выделенном фигурными скобками. Указатель, объявленный в fixed-блоке, доступен только в пределах этого блока. Объект, на поле которого ссылается указатель, не будет удален из памяти до тех пор, пока выполняются команды в fixed-блоке. Программа, в которой иллюстрируется использование инструкции fixed, представлена в примере ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr211_1 { // Класс с полем: class MyClass { // Поле: public int number; // Конструктор: public MyClass(int n) { number = n; } // Деструктор: ~MyClass() { Console.WriteLine("Удален объект с полем " + number); } } // Главный класс программы: class Program { // Главный метод: unsafe static void Main() { // Использован fixed-блок: fixed (int* p = &new MyClass(123).number) { // Отображение значения поля: Console.WriteLine("Значение поля number: " + *p); // Полю присваивается новое значение: *p = 321; // Отображение сообщения: Console.WriteLine("Завершение fixed-блока"); } } } }
Результат выполнения программы такой:
Рис.1. Результат выполнения программы
В программе описывается класс MyClass, у которого есть открытое целочисленное поле number, конструктор с одним аргументом (определяет значение поля), а также деструктор, который при удалении объекта из памяти выводит в консоль соответствующее сообщение со значением поля number удаляемого объекта.
Главный метод состоит из fixed-блока, в котором объявлен указатель p на целочисленное значение, и этому указателю в качестве значения присвоено выражение &new MyClass(123).number. Проанализируем его. Инструкция new MyClass(123) создает объект класса MyClass, и поле number этого объекта равно 123. Значением инструкции является ссылка на созданный объект. Эта ссылка никуда не записывается, и поэтому объект является анонимным. Сразу после создания он попадает в очередь на удаление. Точнее, должен был бы попасть. Инструкцией new MyClass(123).number мы обращаемся к полю number этого объекта, а символ & перед всем выражением означает, что нас интересует адрес этого поля. Таким образом, значением выражения &new MyClass(123).number является адрес поля number созданного анонимного объекта, и этот адрес записывается в указатель p. В итоге объект, хотя он и анонимный, гарантированно не удаляется из памяти, пока выполняется fixed-блок.
Значение поля объекта проверяем с использованием выражения *p. Командой
*p = 321;
На следующем шаге мы рассмотрим связь указателей и массивов.