Сегодня хочу поговорить про property в C++ Builder (и DELPHI)
Итак на повестке дня:
1. Что такое property
2. Как его использовать
3. Вкусный и полезный пример реального использования.
Начнёмс.
1. В Объектно ориентированном программировании, основной завет состоит в том, что мухи отдельно, пиво отдельно. Тоесть, пользователь не должен работать с данными напрямую, все манипуляции должны осуществляться через методы класса. В теории это всё конечно хорошо, но на практике жутко неудобно, для сглаживания неудобства и существуют property. Это конструкция, которая для пользователя вне класса выглядит как переменная, а в классе в зависимости от действий с этой переменной, вызывается нужная функция. Проилюстрирую это примером.
class my1
{
private:
int i;
int ReadI(void);
void WriteI(int newI);
public:
__property int I={read=ReadI,write=WriteI};
}
В данном классе реализованно property I, использоваться оно может так:
my1 my1Var;
my1Var.I=5;
int k=my1Var.I;
В месте кода my1Var.I=5, произойдёт вызов функции WriteI, и в качестве параметра ей передастся 5. Аналогично с чтением k=my1Var.I, вызовется функция ReadI, и результат её выполнения занесётся в переменную k
2. Как использовать property?
Более подробно синтаксис можете посмотреть во встроенной справке, но так как она не изобилует пояснениями, вкрадце расскажу.
property описывается так:
__property <имя типа> <имя самого property> = {действие1=функция1,действие2=функция2,}
Тут небольшой ньюанс. У property часто нет необходимости работать именно через функцию, например в нашем простом примере, на действие read надо просто считать значение с переменной i, в таком случае вместо функции можем сразу указать переменную нужного типа. Например вот так:
__property int I={read=i,write=WriteI};
И ньюанс номер 2. Ненужные действия можно пропустить. Например в нашем случае пользователь не может указывать значение property сам, тогда это будет
выглядеть так:
__property int I={read=i};
property может быть с индексами, тогда во всех функциях надо не забывать индекс в качестве параметра
int ReadI(int Index);
void WriteI(int newI,int Index);
__property int I[int Index]={read=ReadI,write=WriteI};
3. Вкусные и полезные примеры, а нафига это вообще нужно.
Если вы прочитали 2 верхних абзаца, у вас наверно появились уже свои мысли где это может быть удобно. Приведу самый распространённый пример. Очень знакомое вам property visible, которое есть практически у всех визуальных компонентов.
Пользователь снаружи просто меняет переменную visible в значение true или false, а класс при этом изменении должен сделать целую кучу вещей, связанных с отображением экземпляра класса.
Второй пример из жизни:
Например вы работаете с какими то данными в классе и храните их в TList. Работать с TList напрямую, категорически неудобно, постоянно надо преобразовывать тип, сделаем это всё через property
Пусть нам надо хранить структуры вида
struct myStruct
{
int a,b,c;
double i,j,k;
};
class my1
{
private:
TList *myList;
myStruct ReadStruct(int index);//для property
int ReadCount(void);
public:
void AddStruct(myStruct a);//для добавления записей в список;
__property myStruct StructArray[int index]={read=ReadStruct};
__property int StructCount={read=ReadCount};
/*само собой нужен конструктор где выделим под TList память и деструктор где уничтожим. Расписывать не буду ибо совсем элементарщина*/
};
myStruct my1::ReadStruct(int index)
{//тут можно много накрутить, например проверки на выход за диапазон
return *((myStruct *)myList->Items[index]);
};
int my1::ReadCount(void)
{
return myList->Count;
};
void my1::AddStruct(myStruct a)//для добавления записей в список;
{
myStruct *tmp;
tmp=new myStruct;
*tmp=a;
myList->Add(tmp);
}
//
Вот и всё. Теперь можно использовать список, как массив.
my1 myClass1;
myStruct m1;
m1.a=5;
myClass1.AddStruct(m1);
int i= myClass1.StructCount;//i будет равно 1
int j= myClass1.StructArray[0].a; //j будет равно 5
Хочется заметить, что так как чтение и запись происходит записями целиком, то хотя структура такого вида не вызовет ошибку
myClass1.StructArray[0].a=10, но значение a не будет изменено. Так как есть 2 варианта сделать правильно:
а) myStruct m2=myClass1.StructArray[0];
m2.a=10;
myClass1.StructArray[0]=m2;//в случае если определено свойство write у property StructArray
б) работать не со статическими результатами а с указателями. Тоесть, сделать property StructArray не со статическим результатом myStruct, а с динамическим myStruct *
Вот и всё на сегодня.


