Класс Custom

Класс Custom — это один из «лёгких» базовых классов Visual FoxPro, позволяющий создавать не визуальные пользовательские классы. Он имеет ограниченный по сравнению с визуальными классами набор свойств и методов, с которыми вы можете познакомиться в справочной документации. На его основе вы можете создавать классы, решающие различные задачи, не требующие визуализации.
Вы не можете в объявлении этого класса использовать предложение ADD OBJECT; тем не менее класс содержит методы AddObject, NewObject и RemoveObject, что позволяет динамически добавлять в него и удалять не визуальные объекты, например, таймер.
В листинге 11.12 показан код класса ConvertAmount, который предназначен для создания объектов, выполняющих преобразование значения суммы (числа) в наименование, например, число 1286.55 будет преобразовано в строку «Одна тысяча двести восемьдесят шесть рублей 55 коп». В качестве базового класса используется класс Custom.

 Листинг 11.12. Класс ConvertAmount

 
 DEFINE CLASS ConvertAmount as Custom 
 PROTECTED aHundreds[9], aTens[9], aUnits[19]
 * Массив наименований сотен
 aHundreds[1] = 'сто'
 aHundreds[2] = 'двести'
 aHundreds[3] = 'триста'
 aHundreds[4] = 'четыреста'
 aHundreds[5] = 'пятьсот'
 aHundreds[6] = 'шестьсот'
 aHundreds[7] = 'семьсот'
 aHundreds[8] = 'восемьсот'
 aHundreds[9] = 'девятьсот'
 * Массив наименований десятков
 aTens[2] = 'двадцать'
 aTens[3] = 'тридцать'
 aTens[4] = 'сорок'
 aTens[5] = 'пятьдесят'
 aTens[6] = 'шестьдесят'
 aTens[7] = 'семьдесят'
 aTens[8] = 'восемьдесят'
 aTens[9] = 'девяносто'
 * Массив наименований единиц
 aUnits[3] = 'три'
 aUnits[4] = 'четыре'
 aUnits[5] = 'пять'
 aUnits[6] = 'шесть'
 aUnits[7] = 'семь'
 aUnits[8] = 'восемь'
 aUnits[9] = 'девять'
 aUnits[10] = 'десять'
 aUnits[11] = 'одиннадцать'
 aUnits[12] = 'двенадцать'
 aUnits[13] = 'тринадцать'
 aUnits[14] = 'четырнадцать'
 aUnits[15] = 'пятнадцать'
 aUnits[16] = 'шестнадцать'
 aUnits[17] = 'семнадцать'
 aUnits[18] = 'восемнадцать'
 aUnits[19] = 'девятнадцать'

 FUNCTION Convert(tnValue)
    LOCAL lcSumma, lcResult, lnDec
    IF tnValue > 2147483647 .or. tnValue <= 0
       RETURN "Число вне допустимых пределов"
    ENDIF 
    lcSumma = STR(INT(tnValue),12)
    lcResult = this.NumToString(SUBSTR(lcSumma,1,3),"миллиард",3)
    lcResult = lcResult+this.NumToString(SUBSTR(lcSumma,4,3),"миллион",3)
    lcResult = lcResult+this.NumToString(SUBSTR(lcSumma,7,3),"тысяч",2)
    lcResult = lcResult+this.NumToString(SUBSTR(lcSumma,10,3),"рубл",1)
    lnDec = 100 * (tnValue - INT(tnValue))
    RETURN UPPER(LEFT(lcResult,1)) + SUBSTR(lcResult,2) + TRANSFORM(lnDec, "@L 99") + " коп"
 ENDFUNC 
 
 PROTECTED FUNCTION NumToString(tcValue, tcName, tnFlag)
    LOCAL lcResult, lnHundreds, lnTens, lnUnits, lcName, lcExt
    IF tcValue = " "
       IF tnFlag = 1
          RETURN "Ноль рублей "
       ENDIF 
       RETURN ""
    ENDIF 
    IF tnFlag = 2
       this.aUnits[1] = "одна"
       this.aUnits[2] = "две"
    ELSE 
       this.aUnits[1] = "один"
       this.aUnits[2] = "два"
    ENDIF 
    lcResult = ""
    lnHundreds = VAL(LEFT(tcValue,1))   && Выделяем количество сотен
    lnTens = VAL(SUBSTR(tcValue,2,1))   && ... десятки
    lnUnits = VAL(SUBSTR(tcValue,3,1))  && ... единицы (от 1 до 19)
    IF lnHundreds > 0
       lcResult = lcResult + this.aHundreds[lnHundreds] + " "
    ENDIF 
    IF lnTens > 1
       lcResult = lcResult + this.aTens[lnTens] + " " + this.aUnits[lnUnits] + " "
    ELSE 
       lcResult = lcResult + this.aUnits[10 * lnTens + lnUnits] + " "
    ENDIF 
    lcResult = lcResult + tcName
 *
 * Формирование окончаний
 *
    lcExt = ""
    DO CASE 
       CASE tnFlag = 1 && Рубли (единицы)
          lcExt = "ей" && По умолчанию "рублей"
          IF lnTens != 1
             DO CASE 
                CASE lnUnits = 1 && 1; 21; 31 ...
                   lcExt = "ь" && "рубль"
                CASE lnUnits > 1 .and. lnUnits < 5
                   lcExt = "я"
             ENDCASE 
          ENDIF 
       CASE tnFlag = 2 && Тысячи
          IF lnTens != 1
             DO CASE 
                CASE lnUnits = 1
                   lcExt = "а"
                CASE lnUnits > 1 .and. lnUnits < 5
                   lcExt = "и"
             ENDCASE 
          ENDIF 
       CASE tnFlag = 3 && Миллионы, миллиарды
          lcExt = "ов" && По умолчанию "миллионов"
          IF lnTens != 1
             DO CASE 
                CASE lnUnits = 1
                   lcExt = ""
                CASE lnUnits > 1 .and. lnUnits < 5
                   lcExt = "а"
             ENDCASE 
          ENDIF 
    ENDCASE 
    RETURN lcResult + lcExt + " "
 ENDFUNC 
 ENDDEFINE
 

В классе объявляются защищённые массивы, содержащие наименования чисел, и два метода — открытый метод Convert, которому передаётся преобразуемое число, и защищённый NumToString. Как выполняется преобразование числа в строку, понятно из кода метода.
Для использования класса нужно создать объект и вызвать его метод Convert:

 
 oCnv = CREATEOBJECT("ConvertAmount")
 cString = oCnv.Convert(1286.55)
 

Класс Collection

Что такое коллекции? Коллекция — это инструмент для объединения связанных объектов в группы. Например, объект Form поддерживает коллекцию объектов — управляющих элементов Controls и имеет соответствующие методы для добавления новых элементов в коллекцию или удаления их из неё. Вы можете обратиться к любому элементу в коллекции Controls по его индексу, например:

 
 thisform.Controls[nIndex].Name
 

Свойство ControlCount формы содержит значение, равное количеству объектов в коллекции. При удалении объекта из коллекции, независимо от его положения внутри коллекции, значение свойства ControlCount уменьшается на единицу, а индексы всех объектов, следующих в коллекции за удаляемым объектом, так же уменьшаются на единицу.
Коллекции позволяют организовать объектно-ориентированный подход к хранению данных. Кроме того, в отличие от массивов, коллекции не требуют повторного задания размеров в случае добавления или удаления элементов.
В Visual FoxPro для создания коллекций применяется базовый класс Collection. В объекте — экземпляре этого класса вы можете хранить не только объекты, но и данные любых других типов: числовые, символьные, логические, и даже массивы. К каждому элементу коллекции можно обратиться как по его индексу, так и по ключу — строке, как и индекс, однозначно идентифицирующей этот элемент. В отличие от массивов, вы можете добавлять новые элементы в коллекцию, размещая их между любыми уже существующими элементами.

Создание

Для создания объекта — экземпляра класса Collection можно использовать функции CREATEOBJECT() или NEWOBJECT(), например:

 
 oCollection = CREATEOBJECT('Collection')
 

Вы можете добавлять объекты Collection в свои классы, используя команду ADD OBJECT, либо динамически во время выполнения в уже существующие объекты-контейнеры при помощи методов AddObject или NewObject.
Вы так же можете использовать класс Collection как класс-родитель при объявлении собственных классов.

Свойства

Свойство Count содержит количество элементов коллекции. Оно доступно только для чтения:

 
 nCount = oCollection.Count
 

Свойство KeySort определяет, в какой последовательности Visual FoxPro перечисляет элементы коллекции в цикле FOR EACH.

Таблица 11.13. Значения свойства KeySort класса Collection

KeySort Описание
0 Сортировка по возрастанию значения индекса (используется по умолчанию)
1 Сортировка по убыванию значения индекса
2 Сортировка по возрастанию значения ключа
3 Сортировка по убыванию значения ключа

В листинге 11.13 показано применение свойства KeySort.

 Листинг 11.13. Применение свойства KeySort объекта Collection

 
 oCollection.KeySort = 1
 FOR EACH oItem IN oCollection
    ? IIF(VARTYPE(oItem) = 'O', oItem.Name, oItem)
 ENDFOR
 

Остальные свойства класса Collection — общие для всех базовых классов Visual FoxPro; при необходимости вы найдёте их описание в справочной документации.

Методы

Из всех методов класса мы рассмотрим здесь методы Add, Item, GetKey и Remove. Описание остальных методов класса вы можете найти в справочной документации.

Метод Add

Метод добавляет в коллекцию новый элемент. Вот его синтаксис:

 
 Collection.Add( eItem [, cKey [, [eBefore |, eAfter ]]] )
 

Параметр eItem представляет собой выражение любого типа, которое добавляет элемент в коллекцию.
Необязательный параметр cKey определяет ключ — символьную строку, которая будет идентифицировать объект. Ключи должны быть уникальны, то есть не допускается использование двух одинаковых символьных строк как ключей для нескольких объектов коллекции.
Необязательные параметры eBefore и eAfter определяют положение добавляемого элемента в коллекции. Параметр eBefore указывает ключ, перед которым должен быть помещён добавляемый элемент, а параметр eAfter — ключ, после которого должен быть помещён добавляемый элемент. Вы можете указать одновременно оба параметра либо один из них; если вы опускаете параметр eBefore, то его место должно быть обозначено в списке параметров запятой:

 
 oCollection.Add('Орхидея', 'Цветок1', , 'Цветок3')
 

В листинге 11.14 показано применение метода Add.

 Листинг 11.14. Применение метода Add объекта Collection

 
 LOCAL oItems AS Collection
 oItems = NEWOBJECT("Collection")
 oItems.Add("Орхидея", "Цветок2")
 oItems.Add("Роза", "Цветок1", "Цветок2")
 oItems.Add("Колокольчик", "Цветок3")
 ? oItems.Item[1],' ',oItems.GetKey(1)
 ? oItems.Item[2],' ',oItems.GetKey(2) 
 ? oItems.Item[3],' ',oItems.GetKey(3) 
 

В результате выполнения этого кода вы получите следующий список:

Роза Цветок1
Орхидея Цветок2
Колокольчик Цветок3

Почему вывод произведён именно в указанной последовательности? Действительно, после добавления в коллекцию строки "Орхидея" индекс этого элемента получил значение, равное единице. Но при добавлении следующего элемента было указано, что он должен быть размещён перед элементом с ключом "Цветок2". Таким образом, элемент "Роза" получил значение индекса, равное единице, а индекс элемента "Орхидея" стал равен двум. При добавлении следующего элемента (строки "Колокольчик") его положение явно не указано, поэтому этот элемент был добавлен в конец коллекции.

Замечание
Если вы хотите использовать параметр cKey хотя бы для одного элемента коллекции, то тогда вы должны использовать этот параметр для всех элементов коллекции; при нарушении этого требования возникает ошибка времени выполнения.

Метод Item

Метод позволяет обратиться к элементу коллекции по его индексу. Вот его синтаксис:

 
 oCollection.Item(eIndex)
 

где eIndex может быть либо числовым значением индекса элемента, либо наименованием ключа. Пример применения метода показан в листинге 11.15.

Листинг 11.15. Применение метода Item объекта Collection

 

 
 LOCAL oItems AS Collection, oForm AS Form
 oForm = NEWOBJECT("Form")
 oForm.Name = "MyForm"
 oCollection = NEWOBJECT("Collection")
 oCollection.Add(oForm, "Это форма")
 ? oCollection.Item(1).Name               && Обращение по индексу
 ? oCollection.Item("Это форма").Name     && Обращение по ключу
 ? oCollection(1).Name                    && Использование метода Item по умолчанию
 ? oCollection("Это форма").Name          && то же
  

Последние четыре строки кода демонстрируют варианты обращения к элементу коллекции. Все они возвращают одинаковый результат.

Метод GetKey

Метод позволяет получить значение ключа по индексу элемента коллекции либо значение индекса по ключу. Если элемента с указанным индексом (или ключом) нет в коллекции, метод возвращает ноль. Вот синтаксис этого метода:

 
 oCollection.GetKey(eIndex)
 

В табл. 11.14 перечислены возвращаемые методом значения.

Таблица 11.14. Значения, возвращаемые методом GetKey

Возвращаемое значение Значение параметра eIndex Описание
Ключ (строка) Индекс (число) Возвращает значение ключа элемента по его индексу
Индекс (число) Ключ (строка) Возвращает индекс элемента по значению его ключа
Пустая строка Индекс (число) Если указанный индекс не существует, или если элемент не имеет ключа
0 (ноль) Ключ (строка) Если указанный индекс не существует

Метод Remove

Метод удаляет элемент из коллекции. Вот его синтаксис:

 
 oCollection.Remove(eIndex)
 

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

Применение

Применение коллекций позволит вам решить множество проблем, которые возникают при хранении данных в массивах. Простота использования и объектно-ориентированный подход являются определяющими факторами в пользу применения коллекций.
В листинге 11.16 приведён демонстрационный пример кода из справочной документации, в котором показано применение коллекции для хранения ссылок на объекты — управляющие элементы формы.

 

 Листинг 11.16. Демонстрационный пример использования коллекций

 
 LOCAL loForm, loItem, lnTop
 loForm = CREATEOBJECT("myForm")
 lnTop=0
 FOR EACH loItem IN loForm.myCollection
    TRY
       loItem.Top = lnTop
       lnTop=lnTop + 20
       ? loItem.Name
    CATCH
    ENDTRY
 ENDFOR
 loForm.Show(1)
 
 DEFINE CLASS myForm AS Form
    AllowOutput=.F.
    AutoCenter=.T.
    ADD OBJECT myTextBox1 AS TextBox
    ADD OBJECT myTextBox2 AS TextBox
    ADD OBJECT myButton1 AS CommandButton
    ADD OBJECT myButton2 AS CommandButton
    ADD OBJECT myCollection AS col1
 ENDDEFINE
 
 DEFINE CLASS col1 AS Collection
    PROCEDURE Init
       FOR i = 1 TO THISFORM.Objects.Count
          THIS.Add(THISFORM.Objects(m.i))
       ENDFOR
    ENDPROC 
 ENDDEFINE
 

Класс Empty

Класс Empty используется для создания объектов, предназначенных для хранения данных. В отличие от ранее рассмотренных классов, этот класс не может использоваться как родительский при создании пользовательских классов. Он не содержит никаких свойств и методов; вы можете добавлять новые свойства в объекты этого класса, используя функцию ADDPROPERTY(). Удалить добавленное в этот объект свойство можно, используя функцию REMOVEPROPERTY(). Объект — экземпляр класса Empty может быть создан командой SCATTER … NAME:

 
 SELECT MySessionTable
 SCATTER FIELDS CookieText, SessionId NAME oCustomer ADDITIVE
 ? oCustomer.CookieText
 ? oCustomer.SessionId