Главная » C# » Директивы препроцессора в Visual C# (Sharp)

0

Ранее в этой главе рассматривалось использование символа # и условных оператов для включения или исключения кода из компиляции. На техническом жаргоне это называется предварительной обработкой (preprocessing) кода, а операторы нываются директивами препроцессора. В табл. 6.1 приведен список директив прроцессора.

Таблица        6.1.      Директивы         препроцессора

Директива

Описание

#define

Применяется для определения  идентификаторов  компиляции,  таких как идентификатор  INTEGRATE_TESTS,  использованный  в  примере этой  глы.  Определение выполняется в начале файла исходного кода, чтобы активировать условные  операторы  препроцессора,  используемые  во  всем файле исходного кода.  Область видимости директивы  #def ine огранича одним  файлом исходного кода

#undef

Применяется  для  отмены  определения  идентификатора.  Директива

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

Таблица      6.1      (окончание)

Директива

Описание

#if и  #еп- dif

Используются для условного включения или  исключения  фрагмента  иодного  кода.  Условные директивы  применяются для определения  разных конфигураций  исходного  кода,  например,  определения отладочной  или промышленной  сборки

#elif

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

#else

Определяет блока кода,  который включается, если операторы  #if не аивируются

#region и

#endregion

Данные директивы  не  имеют никакого отношения  к  компиляции  исходно  кода.  Они  применяются Visual  Studio для создания областей исходно  кода,  которые  можно  "сворачивать"  подобно  сворачиванию  дерева каталогов.  Таким  образом,  можно  временно скрыть  блок  кода.  Постояое  прокручивание длинных файлов  исходного  кода  не дает ничего  в терминах  производительности.  Свернув  код,  мы  можем  уменьшить  обм  прокрутки  или совсем устранить ее,  а свернутый  код можно  развеуть в любое  время

В  следующем  фрагменте  кода демонстрируется  использование директив  препрессора:

#define ACTIVATE_1

#undef ACTIVATE_2

namespace TestDefine { class Example {

#if ACTIVATE_1

int _dataMember;

#elif  !N0_ACTIVATE_10 int _dataMember3;

#else

int _defaultValue;

#endif

}

}

В общем, директивьг препроцессора применяются при создании отладочной или конечной версии кода. Отладочная версия означает, что код компилируется таким образом, что его можно отлаживать и анализировать с помощью так называемых декоративных символов. Конечная версия означает, что скомпилированный код также можно отлаживать, но декоративные символы в нем менее информативны.

ПРИМЕЧАНИЕ

Visual   С#  Express  не   позволяет  выбрать  отладочную   или  конечную  версию  разрабываемог о  кода.  Дл я  этой  возможности  необходима  полная  версия  Visual  Studio.

Декоративные символы можно сравнить с информационными дорожными знаками. При исполнении в отладочном режиме эти знаки предоставляют обширную иормацию, типа: "Сейчас вызывается такой-то метод в таком-то файле исходного кода" или "Оба-на, этот код свойства в исходном файле не работает должным обром". В конечной версии декоративные символы предоставляют лишь краткую информацию,  типа  "До  города X осталось  у  километров".  Хотя  эта  информация и лучше чем ничего, тем не менее, она очень ограничена, например, она ничего не говорит нам о промежуточных населенных пунктах.

Область видимости

В примерах этой главы область видимости блоков get и set свойств всегда была одинаковой. Но это не является обязательным, и эти блоки могут иметь разную оасть видимости. Разная область видимости устанавливается с целью разрешить реализацию логики, когда классам в цепочке наследования разрешается  присваать свойству значения, а классам вне этой цепочки разрешается только чтение свойства. Вот пример установки блока set свойства как protected, а блока get — как  public:

class PropertyScopeExample { int _value;

public int Value { protected set {

_value = value;

}

get {

return _value;

}

}

}

Ключевое слово abstract

В примерах этой главы ключевое слово abstract использовалось для объявления класса, к которому можно обращаться, но из которого нельзя создавать экземпляры. Кроме этого назначения, с помощью ключевого слова abstract можно определять методы с отложенной реализацией. Целью такого объявления является позволить разработчику определить  свое намерение в базовом классе, а реализовать в проиодном классе. В реализации классов HotelCurrencyTrader И ActiveCurrencyTrader были определены два метода:  ConvertTo О И ConvertFrom*).  Разработчик мог бы

решить, что эти классы содержат общие методы, которые можно было бы опрелить в базовом классе CurrencyTrader. Это неплохая идея. Вернемся к классу CurrencyTrader и добавим в него эти два  метода  как  абстрактные  (выделено  жиым  шрифтом):

public abstract class CurrencyTrader { private double _exchangeRate;

protected double ExchangeRate { get {

return _exchangeRate;

}

set {

_exchangeRate = value;

}

}

protected double ConvertValue(double input) { return _exchangeRate * input;

}

protected double ConvertValuelnverse(double input) { return input / _exchangeRate;

}

public abstract double ConvertTo(double value); public abstract double ConvertFrom(double value);

}

Класс, в котором объявляется абстрактный метод с отложенной реализацией, также необходимо  объявить  абстрактным.

Любой класс, который наследуют от CurrencyTrader, должен реализовать методы ConvertToо И ConvertFrom(). В случае классов HotelCurrencyTrader И Ac- tiveCurrencyTrader это не является проблемой, т. к. в них эти методы уже реалованы.  Но  их необходимо слегка изменить,  как  показано здесь:

public override double ConvertTo(double value) {

//  …

}

public override double ConvertFrom(double value) {

II…

}

Модификация методов заключается в добавлении в их объявление ключевого слова override, чтобы указать, что функциональность, реализованная в этих методах, подменяет  функциональность  класса  CurrencyTrader.

В то время как мы рассмотрели все технические аспекты абстрактных  методов, остается  открытым   вопрос,  зачем   вообще  нам   нужна  эта  возможность.   Вернемся

к реализации класса ActiveCurrencyTrader, в котором абстрактные методы не реизуются. Чтобы использовать этот класс и метод ConvertToO, можно  написать такой  код:

ActiveCurrencyTrader els = new ActiveCurrencyTrader(); double converted = els.ConvertTo(100.0);

Теперь представьте себе ситуацию, когда значения  для преобразования даны в тетовом представлении. Для общей возможности обработки  можно  применить такой код:

public double ConvertToTextField(ActiveCurrencyTrader els) { return  els.ConvertTo(int.Parse(textl.Text));

}

Но    реализация    метода    ConvertToTextField ()     содержит   серьезную    ошибку — в нем предполагается, что преобразование всегда будет выполняться с помощью класса ActiveCurenceTrader. Если бы мы захотели использовать класс HotelcurrencyTrader, то нам пришлось бы реализовать другой  метод,  с  парамеом  типа  HotelcurrencyTrader.

Данная ситуация является классическим примером проблемы полиморфизма, для решения которой и применяются абстрактные методы. Посмотрите на модифицованный  метод ConvertToTextField ():

public double ConvertToTextField(CurrencyTrader els) { return  els.ConvertTo(int.Parse(textl.Text));

}

В данной реализации метода ConvertToTextField () используется базовый класс CurrencyTrader, а Т. К. метод ы ConvertToO И ConvertFrom() объявлены , ТО К НИМ можно  обращаться.

Каким образом  вызывается  метод  ConvertToTextField ()  и  создается  экземпляр класс а HotelcurrencyTrader ИЛИ класс а ActiveCurrencyTrader, В ЭТОЙ глав е раматриваться не будет, т. к. полиморфизм обсуждается в следующей главе. Когда будете читать эту  главу,  в  которой  рассматриваются  такие  понятия,  как  компонеы и интерфейсы, то просто имейте в виду, что применением  ключевого  слова abstract достигается то же самое, что и с помощью этих концепций.

Советы разработчику

В этой главе мы рассмотрели некоторые основньге понятия объектно-ориентированного программирования. Из этого материала рекомендуется запомнить следующие клевые  аспекты.

•   Код состоит из  структурной  функциональности  (или  функциональности  базово класса) и архитектурной функциональности, связанной с определенной  облтью  деятельности.

•    Функциональность базового класса направлена на решение определенной прлемы. Проблема может быть решена и общим способом, но это решение будет применимо только к данной проблеме. Функциональность базового класса трует от разработчика специфических знаний в данной области. Это можно раматривать как реализацию калькулятора, при которой главной задачей разротчика является обеспечение правильности вычислений.

•    Архитектурная рабочая функциональность является функциональностью более высокого уровня, для реализации которой требуется общее знание области деельности. Идея состоит в том, чтобы с помощью базовых классов решить прлему из определенной области деятельности. Это можно рассматривать как использование калькулятора, где вашей главной задачей является получить рультаты вычислений.

П Свойства необходимо использовать, не предоставляя к их членам данных общо доступа.

О Многие программисты относятся к свойствам отрицательно, т. к. они считают, что использование свойств способствует распространению плохих навыков прраммирования. Следует смотреть дальше такого отношения к свойствам и раматривать их как механизм для предотвращения прямого доступа, аналогично тому, как вы предотвращаете прямой доступ кассиру к вашему бумажнику. Свойства имеют свое назначение и пользу.

•    В общем, классы не должны представлять доступ к своему состоянию. Чтобы не представлять состояние, нужно создавать методы, которые реализуют общее предназначение класса.

D Наследование является фундаментальной частью С#, и необходимо знать, как пользоваться ею. Одним из способов реализации наследования является исполование ключевого слова abstract.

П Подмена (overriding) означает, что мы оставляем интерфейс без изменений, но модифицируем поведение.

П Перегрузка (overloading) означает, что в производном классе определяется такой же идентификатор, как и идентификатор какого-либо базового класса. Перегренный компонент может использоваться и работать по-другому.

П Операторы условной компиляции применяются для включения или исключения конкретных блоков кода в определенные версии приложения.

О Частичные классы применяются для обособления функциональности, имеющей специальное назначение. В примере данной главы частичные классы использались для добавления тестового кода без нарушения запрета на предоставление состояния.

Источник: Гросс  К. С# 2008:  Пер. с англ. — СПб.:  БХВ-Петербург, 2009. — 576 е.:  ил. — (Самоучитель)

По теме:

  • Комментарии