Главная страница
Навигация по странице:

  • Объявление пространства имен

  • Пространства имен предотвращают конфликты по совпадению имен

  • Вторая форма использования директивы using

  • Аддитивность пространств имен

  • Пространства имен могут быть вложенными

  • Пространство имен по умолчанию

  • Компоновочные файлы и модификатор доступа internal

  • Модификатор доступа internal

  • Справочник по C# Герберт Шилдт ббк 32. 973. 26018 75 Ш57 удк 681 07 Издательский дом "Вильямс" Зав редакцией


    Скачать 5.05 Mb.
    НазваниеСправочник по C# Герберт Шилдт ббк 32. 973. 26018 75 Ш57 удк 681 07 Издательский дом "Вильямс" Зав редакцией
    АнкорC #.pdf
    Дата08.12.2017
    Размер5.05 Mb.
    Формат файлаpdf
    Имя файлаC #.pdf
    ТипСправочник
    #10795
    страница27 из 52
    1   ...   23   24   25   26   27   28   29   30   ...   52
    Глава 16
    Пространства имен,
    препроцессор и
    компоновочные файлы

    432
    Часть I. Язык C# этой главе рассматриваются три C#-средства, которые позволяют влиять на организацию и доступность программы. Речь пойдет о пространствах имен, препроцессоре и компоновочных файлах.
    Пространства имен
    О пространствах имен кратко упоминалось в главе 2, поскольку это одно из основополагающих понятий C#: каждая C# -программа так или иначе использует некоторое пространство имен. До сих пор мы не затрагивали эту тему, поскольку C# автоматически предоставляет программе пространство имен по умолчанию. Таким образом, программы, приведенные в предыдущих главах, просто использовали стандартное пространство имен.
    Но реальным программам придется создавать собственные или взаимодействовать с другими пространствами имен. Поэтому настало время поговорить о них более подробно.
    Пространство имен определяет декларативную область, которая позволяет отдельно хранить множества имен. По существу, имена, объявленные в одном пространстве имен, не будут конфликтовать с такими же именами, объявленными в другом. Библиотека .NET
    Framework (которая является C#-библиотекой) использует пространство имен
    System
    Поэтому в начало каждой программы мы включали следующую инструкцию: using System;
    Как было показано в главе 14, классы ввода-вывода определяются внутри пространства имен, подчиненного
    System
    , и именуемого
    System.IO
    . Существуют и другие пространства имен, подчиненные
    System
    , которые включают иные части C#- библиотеки.
    Возникновение пространств имен продиктовано самой жизнью, поскольку в течение последних лет для программирования характерен взрывоподобный рост количества имен переменных, методов, свойств и классов, которые используются в библиотечных процедурах, приложениях сторонних изготовителей ПО и программах, написанных отдельными программистами. Без использования пространств имен все эти имена боролись бы за место “под солнцем” в глобальном пространстве имен, что привело бы к росту числа конфликтов. Например, если в программе определяется класс
    Finder
    , это имя обязательно будет конфликтовать с именем другого класса,
    Finder из библиотеки стороннего приложения, которое использует ваша программа. К счастью, благодаря пространствам имен, проблем такого рода можно избежать, поскольку пространство имен локализует видимость имен, объявленных внутри него.
    Объявление пространства имен
    Пространство имен объявляется с помощью ключевого слова namespace
    . Общая форма объявления пространства имен имеет следующий вид: namespace
    имя
    {
    //
    Члены
    }
    Здесь элемент
    имя
    означает имя пространства имен. Все, что определено внутри пространства имен, находится в пределах его области видимости. Следовательно, пространство имен определяет область видимости. Внутри пространства имен можно объявлять классы, структуры, делегаты, перечисления, интерфейсы или другое пространство имен.
    В

    Глава 16. Пространства имен, препроцессор и компоновочные файлы
    433
    Рассмотрим пример использования ключевого слова namespace, которое создает пространство имен
    Counter
    . Оно ограничивает распространение имени, используемого для реализации класса обратного счета, именуемого
    CountDown
    // Объявление пространства имен для счетчиков. namespace Counter {
    //
    Простой счетчик для счета в обратном направлении. class CountDown { int val; public CountDown(int n) { val = n;
    } public void reset(int n) { val = n;
    } public int count() { if(val > 0) return val--; else return 0;
    }
    }
    }
    Здесь класс
    CountDown объявляется внутри области видимости, определенной пространством имен
    Counter
    А теперь рассмотрим программу, которая демонстрирует использование пространства имен
    Counter
    // Демонстрация использования пространства имен. using System;
    // Объявляем пространство имен для счетчиков. namespace Counter {
    //
    Простой счетчик для счета в обратном направлении. class CountDown { int val; public CountDown(int n) { val = n; } public void reset(int n) { val = n;
    } public int count() { if(val > 0) return val--; else return 0;
    }
    }
    } class NSDemo { public static void Main() {
    Counter.CountDown cd1 = new Counter.CountDown(10); int i; do
    {

    434
    Часть I. Язык C# i
    = cd1.count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine();
    Counter.CountDown cd2 = new Counter.CountDown(20); do
    { i = cd2. count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine(); cd2.reset(4); do
    { i = cd2.count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine();
    }
    }
    Вот результаты выполнения этой программы:
    10 9 8 7 6 5 4 3 2 1 0 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 4 3 2 1 0
    Здесь имеет смысл обратить ваше внимание вот на что. Во-первых, поскольку класс
    CountDown объявляется внутри пространства имен
    Counter
    , то при создании объекта класса
    CountDown
    , как показано в следующей инструкции, имя класса необходимо указывать вместе с именем пространства имен
    Counter
    Counter.CountDown cd1 = new Counter.CountDown(10);
    Но если объект
    Counter уже создан, то в дальнейшем называть его (или любой из его членов) полностью (по “имени-отчеству”) необязательно. Таким образом, метод cd1.count()
    можно вызывать без указания имени пространства имен, как показано в этой строке кода: i = cd1.count();
    Пространства имен предотвращают конфликты по совпадению имен
    Основное преимущество использования пространств имен состоит в том, что имена, объявленные внутри одного из них, не конфликтуют с такими же именами, объявленными вне его. Например, в следующей программе создается еще один класс
    CountDown
    , но он находится в пространстве имен
    Counter2
    // пространства имен предотвращают конфликты,
    // связанные с совпадением имен. using System;
    // Объявляем пространство имен для счетчиков. namespace Counter {
    //
    Простой счетчик для счета в обратном направлении. class CountDown { int val; public CountDown(int n) { val
    = n;

    Глава 16. Пространства имен, препроцессор и компоновочные файлы
    435
    } public void reset(int n) { val = n;
    } public int count() { if(val > 0) return val--; else return 0;
    }
    }
    }
    // Объявляем еще одно пространство имен. namespace Counter2 {
    /*
    Этот класс CountDown находится в пространстве имен Counter2 и не будет конфликтовать с одноименным классом, определенным в пространстве имен Counter. */ class CountDown { public void count() {
    Console.WriteLine("Этот метод count() находится в" +
    " пространстве имен Counter2.");
    }
    }
    } class NSDemo { public static void Main() {
    //
    Этот класс CountDown находится в
    // пространстве имен Counter.
    Counter.CountDown cd1 = new Counter.CountDown(10);
    //
    Этот класс CountDown находится в
    // пространстве имен Counter2.
    Counter2.CountDown cd2 = new Counter2.CountDown(); int i; do
    { i
    = cd1.count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine(); cd2.count();
    }
    }
    Результаты выполнения этой программы имеют такой вид:
    10 9 8 7 6 5 4 3 2 1 0
    Этот метод count() находится в пространстве имен Counter2.
    Как подтверждают результаты выполнения этой программы, класс
    CountDown внутри пространства имен
    Counter отделен от класса
    CountDown
    , определенного в пространстве имен
    Counter2
    , и поэтому имена не конфликтуют. Хотя этот пример очень простой, он позволяет понять, как избежать конфликтов при совпадении имен между своим кодом и кодом, написанным другими, поместив собственные классы в определенное пространство имен.

    436
    Часть I. Язык C#
    Ключевое слово using
    Как разъяснялось в главе 2, если программа включает часто встречающиеся ссылки на определенные члены пространства имен, то необходимость указывать имя этого пространства имен при каждом к ним обращении, очень скоро утомит вас. Эту проблему позволяет решить директива using
    . В примерах этой книги использовалась директива using
    , чтобы сделать текущим C#-пространство имен
    System
    , поэтому вы уже с ним знакомы. Нетрудно предположить, что директиву using можно также использовать для объявления действующими пространств имен, создаваемых программистом.
    Существует две формы применения директивы using
    . Первая имеет такой вид: using
    имя
    ;
    Здесь элемент
    имя
    означает имя пространства имен, к которому необходимо получить доступ. С этой формой директивы using вы уже знакомы. Все члены, определенные внутри заданного пространства имен, становятся частью этого (текущего) пространства имен, поэтому их можно использовать без дополнительного упоминания его имени. Директива using должна находиться в начале каждого программного файла, т.е. предшествовать всем остальным объявлениям.
    В следующей программе переработан пример использования счетчиков из предыдущего раздела, чтобы показать, как с помощью директивы using можно чтобы сделать текущим создаваемое программистом пространство имен.
    // Демонстрация использования пространства имен. using System;
    // Делаем текущим пространство имен Counter. using Counter;
    // Объявляем пространство имен для счетчиков. namespace Counter {
    //
    Простой счетчик для счета в обратном направлении. class CountDown { int val; public CountDown(int n) { val = n;
    } public void reset(int n) { val = n;
    } public int count() { if(val > 0) return val--; else return 0;
    }
    }
    } class NSDemo { public static void Main() {
    //
    Теперь класс CountDown можно использовать
    // без указания имени пространства имен.
    CountDown cd1 = new CountDown(10);

    Глава 16. Пространства имен, препроцессор и компоновочные файлы
    437 int i; do
    { i
    = cd1.count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine();
    CountDown cd2 = new CountDown(20); do
    { i
    = cd2.count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine(); cd2.reset(4); do
    { i = cd2.count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine();
    }
    }
    Эта программа иллюстрирует еще один важный момент: использование одного пространства имен не аннулирует другое. При объявлении действующим некоторого пространства имен его имя просто добавляется к именам других, которые действуют в данный момент. Следовательно, в этой программе действуют пространства имен
    System и
    Counter
    Вторая форма использования директивы using
    Директива using обладает еще одной формой применения: using
    псевдоимя
    =
    имя
    ;
    Здесь элемент
    псевдоимя
    задает еще одно имя для класса или пространства имен, заданного элементом
    имя
    . Теперь программу счета в обратном порядке переделаем еще раз, чтобы показать, как создается
    псевдоимя
    Count для составного имени
    Counter.CountDown
    // Демонстрация использования псевдоимени. using System;
    // Создаем псевдоимя для класса Counter.CountDown. using Count = Counter.CountDown;
    // Объявляем пространство имен для счетчиков. namespace Counter {
    //
    Простой счетчик для счета в обратном направлении. class CountDown { int val; public CountDown(int n) { val = n;
    } public void reset(int n) {

    438
    Часть I. Язык C# val = n;
    } public int count() { if(val > 0) return val--; else return
    0;
    }
    }
    } class NSDemo { public static void Main() {
    //
    Здесь Count используется в качестве имени
    // вместо Counter.CountDown.
    Count cd1 = new Count(10); int i; do
    { i
    = cd1.count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine();
    Count cd2 = new Count(20); do
    { i
    = cd2.count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine(); cd2.reset(4); do
    { i = cd2.count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine();
    }
    }
    После того как имя
    Count было определено в качестве еще одного имени для составного имени
    Counter.CountDown
    , его можно использовать для объявления объектов класса
    CountDown без уточняющего указания пространства имен. Например, в нашей программе при выполнении строки
    Count cd1 = new Count(10); создается объект класса CountDown.
    Аддитивность пространств имен
    В одной программе одно и то же пространство имен можно объявить больше одного раза. Это позволяет распределить его по нескольким файлам или даже разделить его внутри одного файла. Например, в следующей программе определяется два пространства имен
    Counter
    . Одно содержит класс
    CountDown
    , второе — класс
    CountUp
    . При компиляции содержимое двух пространств имен
    Counter объединяется в одно.
    // Демонстрация аддитивности пространств имен. using System;

    Глава 16. Пространства имен, препроцессор и компоновочные файлы
    439
    // Делаем "видимым" пространство имен Counter. using Counter;
    // Теперь действующим является первое пространство
    // имен Counter. namespace Counter {
    //
    Простой счетчик для счета в обратном направлении. class CountDown { int val; public CountDown(int n) { val = n;
    } public void reset(int n) { val = n;
    } public int count() { if(val > 0) return val--; else return 0;
    }
    }
    }
    // Теперь действующим является второе пространство
    // имен Counter. namespace Counter {
    // Простой счетчик для счета в прямом направлении. class CountUp { int val; int target; public int Target { get{ return target;
    }
    } public CountUp(int n) { target
    = n; val = 0;
    } public void reset(int n) { target
    = n; val = 0;
    } public int count() { if(val < target) return val++; else return target;
    }
    }
    } class NSDemo { public static void Main() {

    440
    Часть I. Язык C#
    CountDown cd = new CountDown(10);
    CountUp cu = new CountUp(8); int i; do
    { i
    = cd.count();
    Console.Write(i + " ");
    } while(i > 0);
    Console.WriteLine(); do
    { i
    = cu.count();
    Console.Write(i + " ");
    } while(i < cu.Target);
    }
    }
    При выполнении этой программы получаем следующие результаты:
    10 9 8 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8
    Хотелось бы обратить ваше внимание вот на что. Инструкция using Counter; делает “видимым” содержимое обоих пространств имен. Поэтому к методам
    CountDown и
    CountUp можно обращаться напрямую, без уточняющей информации о пространстве имен.
    И тот факт, что пространство имен
    Counter было разделено на две части, не имеет никакого значения.
    Пространства имен могут быть вложенными
    Одно пространство имен можно вложить в другое. Рассмотрим следующую программу:
    // Демонстрация вложенных пространств имен. using System; namespace NS1 { class ClassA { public ClassA() {
    Console.WriteLine("Создание класса ClassA.");
    }
    } namespace NS2 { // Вложенное пространство имен. class ClassB { public
    ClassB()
    {
    Console.
    WriteLine("Создание класса ClassB.");
    }
    }
    }
    } class NestedNSDemo { public static void Main() {
    NS1.ClassA a = new NS1.ClassA();
    // NS2.ClassB b = new NS2.ClassB(); // Ошибка!!!

    Глава 16. Пространства имен, препроцессор и компоновочные файлы
    441
    // Пространство имен NS2 не находится в зоне видимости.
    NS1.NS2.ClassB b = new NS1.NS2.ClassB(); // Здесь все
    // правильно.
    }
    }
    Вот результаты выполнения этой программы:
    Создание класса ClassA.
    Создание класса ClassB.
    В этой программе пространство имен
    NS2
    вложено в пространство имен
    NS1
    Следовательно, при обращении к классу
    ClassB
    его имя необходимо уточнять, указывая оба пространства имен: как
    NS1
    , так и
    NS2
    . Одного лишь имени
    NS2
    недостаточно. Как видно в программе, имена пространств имен разделяются точкой,
    Вложенные пространства имен можно задавать с помощью одной инструкции, но разделив их точками. Например, задание вложенных пространств имен namespace OuterNS { namespace InnerNS {
    // ...
    }
    } можно записать в таком виде: namespace OuterNS.InnerNS {
    //
    }
    Пространство имен по умолчанию
    Если для программы не объявлено пространство имен, используется пространство имен, действующее по умолчанию. Вот почему необязательно было указывать его для программ, приведенных в предыдущих главах. Но если для коротких простых программ
    (подобных тем, что приведены в этой книге) такой стандартный подход вполне приемлем (и даже удобен), большинство реальных программ содержится внутри некоторого пространства имен. Главная причина инкапсуляции программного кода внутри пространства имен состоит в предотвращении конфликтов при совпадении имен.
    Пространства имен — это еще один инструмент, позволяющий программисту так организовать свои программы, чтобы они не теряли жизнеспособности в сложной среде с сетевой структурой.
    Препроцессор
    В C# определен ряд директив препроцессора, которые влияют на способ интерпретации исходного кода компилятором. Эти директивы обрабатывают текст исходного файла, в котором они находятся, еще до трансляции программы в объектный код.
    Директивы препроцессора — в основном “выходцы” из C++, поскольку препроцессор C# очень похож на тот, который определен в языке C++. Термин директива препроцессора
    своим происхождением обязан тому факту, что эти инструкции традиционно обрабатывались на отдельном этапе компиляции, именуемой процессором предварительной
    обработки, или препроцессором (preprocessor). Современная технология компиляторов больше не требует отдельного этапа для обработки директив препроцессором, но название осталось.

    442
    Часть I. Язык C#
    В C# определены следующие директивы препроцессора:
    #define #elif
    #else
    #endif
    #endregion #error
    #if
    #line
    #region #undef #warning
    Все директивы препроцессора начинаются со знака “
    #
    ”, Кроме того, каждая директива препроцессора должна занимать отдельную строку.
    Откровенно говоря, поскольку в C# использована современная объектно- ориентированная архитектура, в директивах препроцессора программисты не испытывают острой необходимости, как это было в языках программирования более ранних поколений.
    Тем не менее время от времени они могут быть полезными, особенно для условной компиляции. Рассмотрим все перечисленные выше директивы.
    #define
    Директива
    #define определяет последовательность символов, именуемую
    идентификатором. С помощью директив
    #if или
    #elif можно определить наличие или отсутствие в программе идентификатора, а результат такой проверки используется для управления компиляцией. Общая форма записи директивы
    #define такова:
    #define
    идентификатор
    Обратите внимание на то, что в инструкции нет завершающей точки с запятой.
    Между самой директивой
    #define и идентификатором может стоять любое количество пробелов, но завершить идентификатор можно только символом новой строки. Например, чтобы определить идентификатор
    EXPERIMENTAL
    , используйте следующую директиву:
    #define EXPERIMENTAL
    На заметку
    В C/C++ директиву #define можно использовать для выполнения текстовых
    подстановок, определяя для заданного значения осмысленное имя, а также для
    создания макросов, действующих подобно функциям. Таков использование
    директивы #define C# не поддерживает. В C# директива #define
    используется только для определения идентификатора.
    #if и #endif
    Директивы
    #if и
    #endif позволяют выполнить условную компиляцию последовательности инструкций программного кода в зависимости от того, истинно ли выражение, включающее одно или несколько идентификаторов. Истинным считается идентификатор, определенный в программе. В противном случае он считается ложным.
    Следовательно, если символ определен с помощью директивы
    #define
    , он оценивается как истинный.
    Общая форма использования директивы
    #if такова:
    #if
    символьное_выражение
    последовательность_инструкций
    #endif
    Если выражение, стоящее после директивы
    #if(
    символьное_выражение
    )
    ,
    истинно, код, расположенный между нею и директивой
    #endif(
    последовательность_инструкций
    )
    , компилируется. В противном случае он опускается. Директива
    #endif означает конец
    #if
    -блока.

    Глава 16. Пространства имен, препроцессор и компоновочные файлы
    443
    Символьное выражение может состоять из одного идентификатора. Более сложное выражение можно образовать с помощью следующих операторов:
    !
    ,
    ==
    ,
    !=
    ,
    &&
    и
    ||
    Разрешено также использовать круглые скобки.
    Рассмотрим пример использования директив
    #if
    ,
    #endif и
    #define
    // Демонстрация использования директив #if, #endif
    // и #define.
    #define EXPERIMENTAL using System; class Test { public static void Main() {
    #if EXPERIMENTAL
    Console.WriteLine(
    "Компилируется для экспериментальной версии.");
    #endif
    Console.WriteLine(
    "Эта информация отображается во всех версиях.");
    }
    }
    При выполнении программы отображаются следующие результаты:
    Компилируется для экспериментальной версии.
    Эта информация отображается во всех версиях.
    В этой программе с помощью директивы
    #define определяется идентификатор
    EXPERIMENTAL
    . Поэтому при использовании директивы
    #if символьное выражение
    EXPERIMENTAL
    оценивается как истинное, и компилируется первая (из двух)
    WriteLine()
    -инструкция. Если удалить определение идентификатора experimental и перекомпилировать программу, первая
    WriteLine()
    -инструкция не скомпилируется, поскольку результат выполнения директивы
    #if будет оценен как ложный. Вторая
    WriteLine()
    -инструкция скомпилируется обязательно, поскольку она не является частью
    #if
    -блока. Как упоминалось выше, в директиве
    #if можно использовать символьное выражение. Вот пример:
    // Использование символьного выражения.
    #define EXPERIMENTAL
    #define TRIAL using System; class Test { public static void Main() {
    #if EXPERIMENTAL
    Console.WriteLine(
    "Компилируется для экспериментальной версии.");
    #endif
    #if EXPERIMENTAL && TRIAL
    Console.Error.WriteLine(
    "Тестирование экспериментальной пробной версии.");
    #endif

    444
    Часть I. Язык C#
    Console.WriteLine(
    "Эта информация отображается во всех версиях.");
    }
    }
    Вот результаты выполнения этой программы:
    Компилируется для экспериментальной версии.
    Тестирование экспериментальной пробной версии.
    Эта информация отображается во всех версиях.
    В этом примере определяются два идентификатора,
    EXPERIMENTAL
    и
    TRIAL
    Вторая
    WriteLine()
    -инструкция компилируется только в случае, если определены оба идентификатора.
    #else и #elif
    Директива
    #else работает подобно else-инструкции в языке C#, т.е. она предлагает альтернативу на случай, если директива
    #if выявит ложный результат. Следующими пример представляет собой расширенный вариант предыдущего.
    // Демонстрация использования директивы #else.
    #define EXPERIMENTAL using System; class Test { public static void Main() {
    #if
    EXPERIMENTAL
    Console.WriteLine(
    "Компилируется для экспериментальной версии.");
    #else
    Console.WriteLine("Компилируется для бета-версии.");
    #endif
    #if EXPERIMENTAL && TRIAL
    Console.Error.WriteLine(
    "Тестирование экспериментальной пробной версии.");
    #else
    Console.Error.WriteLine(
    "Это не экспериментальная пробная версия.");
    #endif
    Console.WriteLine(
    "Эта информация отображается во всех версиях.");
    }
    }
    При выполнении этой программы получены такие результаты:
    Компилируется для экспериментальной версии.
    Это не экспериментальная пробная версия.
    Эта информация отображается во всех версиях.
    Поскольку идентификатор
    TRIAL
    не определен, компилируется
    #else
    -блок второй условной последовательности инструкций.
    Обратите внимание на то, что директива
    #else отмечает одновременно как конец
    #if
    -блока, так и начало
    #else
    -блока, поскольку с любой директивой
    #if может быть связана только одна директива
    #endif

    Глава 16. Пространства имен, препроцессор и компоновочные файлы
    445
    Директива
    #elif означает “иначе если” и используется в if-else-if
    -цепочках многовариантной компиляции. С директивой
    #elif связано символьное выражение. Если оно истинно, следующий за ним блок кода
    (
    последовательность_инструкций
    )
    компилируется, и другие
    #elif
    -выражения не проверяются, В противном случае тестируется следующий
    #elif
    -блок. Общая форма цепочки
    #elif
    -блоков имеет следующий вид:
    #if
    символьное_выражение
    последовательность_инструкций
    #elif
    символьное_выражение
    последовательность_инструкций
    #elif
    символьное_выражение
    последовательность_инструкций
    #elif
    символьное_выражение
    последовательность_инструкций
    #elif
    символьное_выражение
    #endif
    Рассмотрим пример:
    // Демонстрация использования директивы #elif.
    #define RELEASE using System; class Test { public static void Main() {
    #if EXPERIMENTAL
    Console.WriteLine(
    "Компилируется для экспериментальной версии.");
    #elif
    RELEASE
    Console.WriteLine("Компилируется для бета-версии.");
    #else
    Console.WriteLine(
    "Компилируется для внутреннего тестирования.");
    #endif
    #if TRIAL && !RELEASE
    Console.WriteLine("Пробная версия.");
    #endif
    Console.WriteLine(
    "Эта информация отображается во всех версиях.");
    }
    }
    Результаты выполнения этой программы выглядят так:
    Компилируется для бета-версии.
    Эта информация отображается во всех версиях.
    #undef
    Директива
    #undef аннулирует приведенное выше определение идентификатора, который указан после директивы. Общая форма директивы
    #undef имеет следующий вид:
    #undef
    идентификатор

    446
    Часть I. Язык C#
    Рассмотрим пример:
    #define SMALL
    #if SMALL
    // ...
    #undef SMALL
    // Здесь идентификатор SMALL уже не определен.
    После выполнения директивы
    #undef идентификатор SMALL больше не считается определенным.
    Директива
    #undef используется главным образом для того, чтобы разрешить локализацию идентификатора только в пределах нужных разделов кода.
    #error
    Директива
    #error вынуждает компилятор прекратить компиляцию. Она используется в целях отладки.
    Общая форма директивы
    #error имеет следующий вид:
    #error
    сообщение_об_ошибке
    При использовании директивы
    #error отображается заданное
    сообщение_об_
    ошибке
    . Например, при обработке компилятором строки
    #error Это тестовая ошибка! процесс компиляции будет остановлен, а на экране появится сообщение “Это тестовая ошибка!”.
    #warning
    Директива
    #warning подобна директиве
    #error
    , но она не извещает об ошибке, а содержит предупреждение. Процесс компиляции при этом не останавливается. Общая форма директивы
    #warning имеет следующий вид:
    #warning предупреждающее_сообщение
    #line
    Директива
    #line устанавливает номер строки и имя файла, который содержит директиву
    #line
    . Номер строки и имя файла используются во время компиляции при выводе сообщений об ошибках или предупреждений. Общая форма записи директивы
    #line выглядит так:
    #line
    номер
    "
    имя_файла
    "
    Здесь элемент
    номер
    представляет собой любое положительное целое число, которое станет новым номером строки, а необязательный элемент
    имя_файла
    - любой допустимый идентификатор файла, который станет новым именем файла. Директива
    #line в основном используется при отладке и в специальных приложениях.
    Чтобы вернуть нумерацию строк в исходное состояние, используйте ключевое слово default: #line default

    Глава 16. Пространства имен, препроцессор и компоновочные файлы
    447
    #region и #endregion
    Директивы
    #region и
    #endregion позволяют определить область, которую можно будет разворачивать или сворачивать при использовании интегрированной среды разработки (IDE) Visual Studio. Вот общая форма использования этих директив:
    #region
    имя_области
    // последовательность_инструкций
    #endregion
    Нетрудно догадаться, что элемент
    имя_области
    означает имя области.
    Компоновочные файлы и модификатор доступа
    internal
    Неотъемлемой частью C#-программирования является компоновочный файл
    (assembly), который содержит информацию о развертывании программы и ее версии.
    Компоновочные файлы имеют важное значение для .NET-среды. Согласно документации
    Microsoft, “компоновочные файлы являются строительными блоками среды .NET
    Framework.” Компоновочные файлы поддерживают механизм безопасного взаимодействия компонентов, межъязыковой работоспособности и управления версиями. Компоновочный файл также определяет область видимости.
    Компоновочный файл состоит из четырех разделов. Первый представляет собой
    декларацию (manifest). Декларация содержит информацию о компоновочном файле. Сюда относятся такие данные, как имя компоновочного файла, номер его версии, информация о соответствии типов и параметры “культурного уровня”. Второй раздел включает
    метаданные, или информацию о типах данных, используемых в программе. В числе прочих достоинств метаданных — обеспечение взаимодействия программ, написанных на различных языках программирования. Третий раздел компоновочного файла содержит программный код, который хранится в формате Microsoft Intermediate Language (MSIL).
    Наконец, четвертый раздел представляет собой ресурсы, используемые программой.
    К счастью, при использовании языка C# компоновочные файлы создаются автоматически, без дополнительных (или с минимальными) усилий со стороны программиста. Дело в том, что выполняемый файл, создаваемый в результате компиляции
    C#-программы, в действительности является компоновочным файлом, который содержит выполняемый код программы и другую информацию. Следовательно, при компиляции C#- программы автоматически создается компоновочный файл.
    Подробное рассмотрение компоновочных файлов выходит за рамки этой книги.
    (Компоновочные файлы — неотъемлемая часть .NET-разработки, а не средство языка C#.)
    Но одна часть языка C# напрямую связана с компоновочным файлом: модификатор доступа internal
    . Вот о нем-то и пойдет речь в следующем разделе.
    Модификатор доступа internal
    Помимо модификаторов доступа public
    , private и protected
    , с которыми вы уже встречались в этой книге, в C# также определен модификатор internal
    . Его назначение — заявить о том, что некоторый член известен во всех файлах, входящих в состав компоновочного, но неизвестен вне его. Проще говоря, член, отмеченный модификатором internal
    , известен только программе, но не где-то еще. Модификатор доступа internal чрезвычайно полезен при создании программных компонентов.

    448
    Часть I. Язык C#
    Модификатор internal можно применить к классам и членам классов, а также к структурам и членам структур. Модификатор internal можно также применить к объявлениям интерфейсов и перечислений.
    Совместно с модификатором internal можно использовать модификатор protected
    . В результате будет установлен уровень доступа protected internal
    , который можно применять только к членам класса. К члену, объявленному с использованием пары модификаторов protected internal
    , можно получить доступ внутри его компоновочного файла. Он также доступен для производных типов.
    Рассмотрим пример использования модификатора доступа internal
    // Использование модификатора доступа internal. using System; class InternalTest { internal int x;
    } class InternalDemo { public static void Main() {
    InternalTest ob = new InternalTest(); ob.x = 10; // Доступ возможен: x — в том же файле.
    Console.WriteLine("Значение ob.x: " + ob.x);
    }
    }
    Внутри класса
    InternalTest поле x
    объявлено с использованием модификатора доступа internal
    . Это означает, что оно доступно в программе, как показано в коде класса
    InternalDemo
    , но недоступно вне ее.

    Полный справочник по
    1   ...   23   24   25   26   27   28   29   30   ...   52


    написать администратору сайта