Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Теоретический_курс.doc
Скачиваний:
36
Добавлен:
10.11.2019
Размер:
7.68 Mб
Скачать

Глава 4. Расширенные возможности программирования на языке c#

4.1. Препроцессорные директивы в c# Препроцессорные директивы в c#

Препроцессорные директивы представляют собой команды, которые изменяют код программы и, которые влияют на процесс сборки и выходной результат этой обработки. В компиляторах C и C++ имеется отдельный препроцессор, но в C# такового нет. Однако, компилятор может обрабатывать директивы условной компиляции, но даже их, нельзя использовать для создания макросов. Ещё одно условие — директива препроцессора должна быть единственной в строке.

Директива всегда начинается со знака «#». После этого знака следует имя директивы.

Рассмотрим директивы, которые можно использовать в C# на примере директив для условной компиляции:

#if — используется вместе с директивой #endif. Описывает условие, при котором код между двумя этими командами будет выполнен. Символ, который следует за #if, должен иметь тип Boolean. Например:

#define DEBUG

//...

#if DEBUG

// Компилируется какой-то код

#endif

Если значение DEBUG будет true, код внутри блока, будет скомпилирован. Значение DEBUG будет равен true, только если оно был ранее определён. За это отвечает #define, но о нём чуть позже.

Аналогично можно использовать так:

#define DEBUG

//...

#if (DEBUG == true)

// Компилируется какой-то код

#endif

Или так:

#define DEBUG

//...

#if (DEBUG && XXX)

// Компилируется какой-то код

#endif

Т.е. для проверки можно использовать операторы && и ||, а также группировать с помощью скобок.

#else — директива следует за #if и является аналогом else в блоке условий if:

#define DEBUG

//...

#if (DEBUG && XXX)

// Компилируется какой-то код, если условие верно

#else

// Если условие не верно, то компилируется это код

#endif

За #else всегда следует #endif.

#elif — аналог else if. Используется для создания сложных условных конструкций. С помощью #elif можно организовать switch:

#if DEBUG

// Какой-то код

#elif XXX

// Какой-то код

#else

// Здесь компилируется код, если два условия выше были false

#endif

#elif, также как и #if необходимо задать условие. Если условие не выполняется, то код внутри директивы не будет учитываться при компиляции.

#endif — просто закрывает #if. Если #elif и #else могут и не описываться, то #endif обязательно должен быть описан.

#define — определяет значение. Определённый, таким образом, символ будет возвращать true в директиве #if. Смотрим пример для #if.

Определение должно находиться в этом же файле, выше все конструкций, в которых оно будет использовано. В C# #define отвечает только за определение символов. Больше ничего эта директива не делает.

Полноценный использования вышеописанной директивы #if:

// preprocessor_if.cs

#define DEBUG

#define MYTEST

using System;

public class Program

{

static void Main()

{

#if (DEBUG && !MYTEST)

Console.WriteLine("DEBUG объявлен");

#elif (!DEBUG && MYTEST)

Console.WriteLine("MYTEST объявлен");

#elif (DEBUG && MYTEST)

Console.WriteLine("DEBUG и MYTEST объявлены");

Console.WriteLine("Для продолжения нажмите любую клавишу . . . ");

Console.ReadKey();

#else

Console.WriteLine("DEBUG and MYTEST не объявлены");

#endif

}

}

/* Выведет:

* DEBUG и MYTEST объявлены

* Для продолжения нажмите любую клавишу . . .

*/

#undef — отменяет определение символа. Например:

#define DEBUG

#undef DEBUG

//...

#if DEBUG

// Код не скомпилируется. DEBUG вернёт false

#endif

Пример:

// preprocessor_undef.cs

// Параметр компиляции: /d:DEBUG

#undef DEBUG

using System;

class Program

{

static void Main()

{

#if DEBUG

Console.WriteLine("DEBUG объявлен");

#else

Console.WriteLine("DEBUG не объявлен");

Console.WriteLine("Для продолжения нажмите любую клавишу . . . ");

Console.ReadKey();

#endif

}

}

/* Выведет:

* DEBUG объявлен

* Для продолжения нажмите любую клавишу . . .

*/

#warning — позволяет создавать предупреждение первого уровня из определенного места в коде. Пример:

// preprocessor_warning.cs

// CS1030 ожидается

#define DEBUG

class Program

{

static void Main()

{

#if DEBUG

#warning DEBUG объявлен

#endif

}

}

/* Выведет (на вкладке "Список ошибок" среды разработки Visual Studio 2010):

* DEBUG объявлен

*/

#warning обычно используется в качестве условной директивы. Кроме того, с помощью #error можно создать ошибку, определенную пользователем.

#error — позволяет создавать ошибку первого уровня из определенного места в коде. Пример:

// preprocessor_error.cs

// CS1029 ожидается

#define DEBUG

class MainClass

{

static void Main()

{

#if DEBUG

#error DEBUG объявлен

#endif

}

}

/* Выведет (на вкладке "Список ошибок" среды разработки Visual Studio 2010):

* DEBUG объявлен

*/

#error обычно используется в качестве условной директивы. Кроме того, с помощью #warning можно создать предупреждение, определенное пользователем.

#line — позволяет изменить номер строки компилятора и (необязательно) вывод имени файла для ошибок и предупреждений. В этом примере показано как сообщить о двух предупреждениям, связанных с номерами строк. Директива #line 200 принудительно задаёт для номера строки значение 200 (хотя по умолчанию это строка № 7), до появления следующей директивы #line имя файла во всех сообщениях будет указываться как «Special». Директива #line восстанавливает нумерацию строк по умолчанию, в которой учитываются строки, перенумерованные предыдущей директивой:

// preprocessor_line.cs

using System;

class Program

{

static void Main()

{

#line 200 "Special"

int i; // CS0168 на линии 200

int j; // CS0168 на линии 201

#line default

char c; // CS0168 на линии 10

float f; // CS0168 на линии 11

#line hidden

string s; // CS0168 на линии 13

double d; // CS0168 на линии 14

}

}

/* Выведет (на вкладке "Список ошибок" среды разработки Visual Studio 2010):

* Переменная "X" объявлена, но ни разу не использована

*/

Директиву #line можно использовать в автоматизированном, промежуточном шаге в процессе построения. Например, если строки были удалены из первого файла с исходным кодом, но компилятор по-прежнему должен выдавать результат на основе исходной нумерации строк в файле, строки можно удалить и затем смоделировать исходную нумерацию строк с помощью #line.

Директива #line hidden скрывает последовательные строки от отладчика так, что когда разработчик переходит по коду, любые строки между #line hidden и следующей директивой #line (если, конечно, это не другая директива #line hidden) будут пропущены. Эту возможность можно также использовать для того, чтобы ASP.NET различал пользовательский и созданный компьютером код. Несмотря на то, что в основном эта возможность используется ASP.NET, существует вероятность, что она будет использоваться большим числом генераторов источника.

Директива #line hidden не влияет на имена файлов или номера строк при создании сообщений об ошибках. То есть, в случае ошибки в скрытом блоке компилятор сообщить имя текущего файла и номер строки с ошибкой.

Директива #line filename определяет имя файла, которое должно присутствовать в результатах компилятора. По умолчанию используется фактическое имя файла с исходным кодом. Имя файла необходимо заключить в двойные кавычки (""), и ему должен предшествовать номер строки.

В файле исходного кода может присутствовать любое число директив #line.

#region ― позволяет указать блок кода, который можно разворачивать и сворачивать с помощью функции структурирования в редакторе кода Visual Studio 2010. В больших файлах кода очень удобно сворачивать или скрывать одну или несколько областей, чтобы не отвлекать внимание от той части файла, над которой в настоящее время идет работа. В следующем примере показано, как определить область:

// preprocessor_region.cs

using System;

class Program

{

#region Класс MyProgram

public class MyProgram

{

static void Main()

{

}

}

#endregion

}

Выглядеть в редакторе кода это (свёрнутый регион) будет так:

Рис. 1. Свёрнутый регион с кодом в редакторе кода Visual Studio 2010

В конце блока #region должна присутствовать директива #endregion. Блок #region не может накладываться на блок #if. Однако блок #region можно вложить в блок #if, а блок #if – в блок #region.

#pragma ― передаёт компилятору специальные инструкции для компиляции файла, в котором она содержится. Эти инструкции должны поддерживаться компилятором. Другими словами, директиву #pragma нельзя использовать для создания пользовательских инструкций предварительной обработки. Директива состоит из двух инструкций:

#pragma warning ― позволяет включать или отключать определенные предупреждения. Например:

#pragma warning disable warning-list

#pragma warning restore warning-list

warning-list ― параметры, список номеров предупреждений с разделителями-запятыми. Нужно вводить номер без префикса «CS». Если номера предупреждений не указаны, disable отключает все предупреждения, а restore включает все предупреждения. Пример:

// pragma_warning.cs

using System;

#pragma warning disable 414, 3021

[CLSCompliant(false)]

public class C

{

int i = 1;

static void Main()

{

}

}

#pragma warning restore 3021

[CLSCompliant(false)] // CS3021

public class D

{

int i = 1;

public static void F()

{

}

}

/* Выведет (на вкладке "Список ошибок" среды разработки Visual Studio 2010):

* "D" не требуется атрибут CLSCompliant, так как сборка не имеет атрибута CLSCompliant

*/

#pragma checksum ― создание контрольных сумм файл исходного кода для помощи в отладке страниц ASP.NET. Например:

#pragma checksum "filename" "{guid}" "checksum bytes"

"filename" ― имя файла, для которого требуется отслеживание изменений и обновлений.

"{guid}" ― идентификатор GUID этого файла.

"checksum_bytes" ― шестнадцатеричное значение контрольной суммы. Должно содержать четное количество шестнадцатеричных чисел. В случае нечетного количества при компиляции будет показано предупреждение, а директива не будет обработана.

Отладчик Visual Studio 2010 использует контрольную сумму для проверки правильности исходного кода. Компилятор вычисляет контрольную сумму для файла исходного кода, затем передает результат в файл PDB базы данных программы. После этого отладчик использует PDB для сравнения с вычисленной контрольной суммой.

Это решение не работает для проектов ASP.NET, поскольку контрольная сумма вычисляется для файла исходного кода, а не для ASPX-файла. Для решения этой проблемы функция #pragma checksum обеспечивает поддержку контрольных сумм для страниц ASP.NET.

При создании проекта ASP.NET в Visual C# созданный файл исходного кода содержит контрольную сумму для ASPX-файла, из которого создается файл исходного кода. Затем компилятор записывает эти данные в файл PDB.

Если компилятор не находит директивы #pragma checksum в файле, он вычислят контрольную сумму и записывает значение в файл PDB. Пример:

// pragma_checksumg.cs

using System;

class Program

{

static void Main()

{

#pragma checksum "pragma_checksumg.cs" "{3673e4ca-6098-4ec1-890f-8fceb2a794a2}" "{012345678AB}" // Новая checksum (значения "якобы" настоящие)

}

}