Чтение онлайн

ЖАНРЫ

ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание

Троелсен Эндрю

Шрифт:

Чтобы блокировать переменную ссылочного типа в памяти из небезопасного контекста, в C# предлагается ключевое слово fixed. Оператор fixed устанавливает указатель на управляемый тип и "закрепляет" переменную на время выполнения оператора. Без ключевого слова fixed в применении указателей на управляемые переменные было бы мало смысла, поскольку в результате сборки мусора. такие переменные могут перемещаться непредсказуемым образом. (На самом деле компилятор C# вообще не позволит установить указатель на управляемую переменную, если в операторе не используется ключевое слово fixed.)

Так что, если вы создадите тип 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 Используются для условного пропуска разделов исходного кода (на основе указанных символов компиляции)

Разделы программного кода

Возможно, одной из самых полезных директив препроцессора являются #region и #endregion. Используя эти признаки, вы указываете блок программного кода, который можно скрыть от просмотра и идентифицировать информирующим текстовым маркером. Использование разделов программного кода может упростить обслуживание больших файлов *.cs. Можно, например, создать один раздел для конструкторов типа, другой – для свойств и т.д.

class Car {

 private string petName;

 private int currSp;

#region Constructors

 public Car {…}

 public Car Car(int currSp, string petName) {…}

#endregion

#region Properties

 public int Speed {…}

 public string Name {…}

#endregion

}

При помещений указателя мыши на маркер свернутого раздела вы получите снимок программного кода, спрятанного за соответствующим названием (рис. 9.5).

Рис. 9.5. Разделы программного кода за работой

Условная компиляция

Другой пакет директив препроцессора (#if, #elif, #else, #endif) позволяет выполнить компиляцию блока программного кода по условию, базируясь на предварительно заданных символах. Классическим вариантом использования этих директив является идентификация блока программного кода, который компилируется только при отладке (а не при окончательной компоновке).

Поделиться с друзьями: