Так что, если вы создадите тип Point (сейчас переопределенный, как класс) и захотите взаимодействовать с его членами, то должны записать следующий программный код (иначе возникнет ошибка компиляции).
unsafe public static void Main {
point pt = new Point;
pt.x = 5;
pt.y = 6;
// Фиксация pt, чтобы не допустить перемещения
// или удаления при сборке мусора.
fixed (int* p =&pt.x) {
// Переменная int* используется здесь.
}
// Теперь pt не зафиксирована и может быть убрана
// сборщиком мусора.
Console.WriteLine("Значение Point: {0}", pt);
}
В сущности, ключевое слово fixed позволяет строить операторы, закрепляющие ссылочную переменную в памяти, чтобы ее адрес оставался постоянным на время выполнения оператора. Для гарантии безопасности обязательно фиксируйте ссылки при взаимодействии со ссылочными типами из небезопасного контекста программного кода.
Ключевое слово sizeof
В заключение обсуждения вопросов, связанных с небезопасным контекстом в C#, рассмотрим ключевое слово sizeof. Как и в C(++), ключевое слово C# sizeof используется для того, чтобы выяснить размер в байтах типа, характеризуемого значениями (но не ссылочного типа), и это ключевое слово может использоваться только в рамках небезопасного контекста. Очевидно, что указанная возможность может оказаться полезной при взаимодействии с неуправляемыми API, созданными на базе C. Использовать ее очень просто.
unsafe {
Console.WriteLine("Длина short равна {0}.", sizeof(short));
Console.WriteLine("Длина int равна {0}.", sizeof(int));
Console.WriteLine("Длина long равна {0}.", sizeof(long));
}
Поскольку sizeof может оценить число байтов для любого элемента, производного от System.ValueType, можно получать размеры пользовательских структур. Допустим, мы определили следующую структуру.
struct MyValueType {
public short s;
public int i;
public long l;
}
Тогда ее размеры можно выяснить так.
unsafe {
Console.WriteLine("Длина short равна {0}.", sizeof(short));
Console.WriteLine("Длина int равна {0}.", sizeof(int));
Console.WriteLine("Длина long равна {0}.", sizeof(long));
Console.WriteLine("Длина MyValueType равна {0}."/ sizeof(MyValueType));
}
Исходный код. Проект UnsafeCode размещен в подкаталоге, соответствующем главе 9.
Директивы препроцессора C#
Подобно многим другим языкам из семейства C, в C# поддерживаются различные символы, позволяющие влиять на процесс компиляции. Перед рассмотрением директив препроцессора C# согласуем соответствующую терминологию. Термин "директива препроцессора C#" не вполне точен. Фактически этот термин используется только для согласованности с языками программирования C и C++. В C# нет отдельного шага препроцессора. Директивы препроцессора в C# являются составной частью процесса лексического анализа компилятора.
Так или иначе, синтаксис директив препроцессора C# очень похож на синтаксис соответствующих директив остальных членов семейства C в том, что эти директивы всегда имеют префикс, обозначенный знаком "диез" (#). В табл. 9.4 описаны некоторые из наиболее часто используемых директив (подробности можно найти в документации .NET Framework 2.0 SDK).
Таблица 9.4. Типичные директивы препроцессора C#
| Директивы | Описание |
|---|---|
| #region, #endregion | Используются для обозначения разделов стягиваемого исходного кода |
| #define, #undef | Используются для определения и отмены определения символов условной компиляции |
| #if, #elif, #else, #endif | Используются для условного пропуска разделов исходного кода (на основе указанных символов компиляции) |
Разделы программного кода