Меню сайта
Мини-чат
Чтобы добавить сообщение, необходимо авторизоваться.
Главная » Статьи » Delphi » Примеры на Delphi7

Семь чудес и два фокуса на Delphi (2 часть)
Чудо шестое (Is-Miracle II)

Давайте посмотрим еще на одно, похожее чудо связанное с оператором is. Добавим к нашей группе проектов (ProjectGroup1) новый проект - DLL с именем AllMirrLib, в единственном модуле которого будет следующий код:

library AllMirrLib;
uses
Controls;

function IsControlLib(const anObj: TObject): boolean;
begin
Result := anObj is TControl;
end;

exports
IsControlLib;

Figure 9.


Как вы видите эта библиотека экспортирует только одну очень простую функцию, которая возвращает знечение True в том случае, если ее единственный параметр происходит от TControl и False - в остальных случаях.

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

unit AllMir;

interface
...
implementation

{$R *.DFM}

function IsControlLib(const anObj: TObject): boolean; external 'AllMirrLib.DLL';

Figure 10.

Теперь, как обычно, добавим на форму новую кнопку: 

procedure TfrmAllMiracles.btnIsMrcl2Click(Sender: TObject);
begin
FControl := TControl.Create(nil);
try
if not IsControlLib(FControl) then
ShowMessage('Not a Control');
finally
FreeAndNil(FControl);
end;
end;

Figure 11.

Как вы уже наверное догадались FControl опять окажется не TControl. Найдите в модуле System процедуру _IsClass. Хоть она и написана на ассемблере, нетрудно понять, что в ней происходит - в цикле просматриваются ссылки на классы (сначала собственная - обьекта, а потом - всех предков) и среди них ищется равная правому операнду. Давайте изменим немного процедуру:

procedure TfrmAllMiracles.btnIsMrcl2Click(Sender: TObject);
var
p1, p2: pointer;
begin
FControl := TControl.Create(nil);
try
p1 := pointer(FControl.ClassType);
p2 := pointer(TControl);
if not IsControlLib(FControl) then
ShowMessage('Not a Control');
finally
FreeAndNil(FControl);
end;
end;

Figure 12.


Посмотрите под отладчиком значения p1 и p2 - они равны. Теперь изменим и функцию IsControlLib:

function IsControlLib(const anObj: TObject): boolean;
var
p3,p4: pointer;
begin
p3 := pointer(anObj.ClassType);
p4 := pointer(TControl);
Result := anObj is TControl;
end;

Figure 13.


Здесь тоже поставим точку останова и сравним значения. Переменные p1, p2 и p3 имеют одно и тоже значение, а вот p4 - указывает куда-то ни туда. Проблема в том, что в аппликации и в DLL сосуществуют два разных класса TControl, вот поэтому равества быть и не может.
Косвенное указание на эту проблему в Help'е можно найти в описании метода ClassNameIs. 

Читаем Help:
Use ClassNameIs when writing conditional code based on an object's type or to query objects across modules, or DLLs.

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

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

Чудо седьмое (Miracle with Variants).

Как вы уже догадались, начнем с новой кнопки, которая выполняет следующие действия при нажатии:

procedure TfrmAllMiracles.btnVarMrclClick(Sender: TObject);
var
X,Y,Z: variant;
begin
X := '1';
Y := '2';
Z := 3;
ShowMessage(X+Y+Z);
end;

Figure 14.


Можете ли вы предсказать результат выражения '1'+ '2'+3? Если вы сказали '6', то вы тоже попались. Посмотрим повнимательнее, '1'+ '2' будет... конечно '12', 12+3=15. Это и есть правильный ответ.

Итак, мы увидели семь чудес Delphi, семь - из многих. Это не значит, что они - самые яркие или самые чудесные. Но на них можно многому научиться. Возьмем последнее, только что рассмотренное нами, чудо. Задумайтесь, как Delphi удается сводить в одном выражении значения разных типов? А если один из членов выражения - variant?

Фокус первый (Variant trick)

Читаем Help в разделе "Variants in expressions":
...In a binary operation, if only one operand is a variant, the other is converted to a variant..

Не кажется ли вам это удивительным - variant можно складывать с чем угодно. Например, integer плюс variant - будет variant, а variant можно опять складывать с чем угодно...

Новая кнопка на форме будет выполнять следующие действия: 

procedure TfrmAllMiracles.btnVarTrickClick(Sender: TObject);
var
v: variant;
b: boolean;
i: integer;
s: string;
d: TDatetime;
x: Double;
begin
v:=0;
b := true;
i := 2;
s := '3';
d := StrToDateTime('01/01/01');
x := 5;
v := v+b+i+s+d+x;
ShowMessage(VarToStr(v));
end;

Figure 15.

Не кажется ли вам, что чудо уже то, что этот код компилируется, а ведь он еще и выдает какой-то результат. А ведь все очень просто - "variant можно складывать с чем угодно" и снова получим - variant.

Однажды ко мне обратился один мой знакомый с вопросом нет ли в Delphi чего-то подобного скрытому параметру Self, но для оператора with. Нет - ответил я ему сперва, а потом задумался...

Фокус второй (With-trick)

Предположим у нас есть следующая функция: 

procedure ShowText(sl: TStringList);
begin
ShowMessage(sl.text);
end;

Figure 16.


И кнопка на форме: 

procedure TfrmAllMiracles.btnWithSelfTrickClick(Sender: TObject);
var
sl: TStringList;
begin
sl := TStringList.Create;
try
sl.CommaText := '1,2,3,4,5,6,7,8,9,0';
ShowText(sl);
finally
sl.Free;
end;
end;

Figure 17.

И мы, по каким-то причинам, хотим избавиться от локальной переменной sl. Но для того, что бы обратиться к функции ShowText, мы должны передать ей параметр типа TStringList. Откуда же его взять?

Давайте порассуждаем. Каждый метод получает скрытый параметр Self, может быть как-то можно вытащить его оттуда? Писать для этого специальный метод какого-то класса не хотелось бы - ведь это работало бы только для его потомков.

Давайте почитаем Help, раздел "TMethod type":
...This type can be used in a type cast of a method pointer to access the code and data parts of the method pointer...

Не это ли то, что мы ищем?
Определим тип и функцию: 

type
TSimpleMethod = procedure of object;

function GetWithSelf(const pr: TSimpleMethod): TObject;
begin
Result := TMethod(pr).Data;
end;

Figure 18.


Как видите, функция принимает указатель на метод, а возвращает обьект, являющийся владельцем этого метода. Но каким же методом мы воспользуемся? Например, метод Free, ведь его история восходит еще к самому TObject'у. Теперь проверим себя:

procedure TfrmAllMiracles.btnWithSelfTrickClick(Sender: TObject);
begin
with TStringList.Create do
try
CommaText := '1,2,3,4,5,6,7,8,9,0';
ShowText(TStringList(GetWithSelf(Free)));
finally
Free;
end;
end;

Figure 19.


Проверьте - работает.
Категория: Примеры на Delphi7 | Добавил: DelphiAiX (09.10.2011)
Просмотров: 1344 | Рейтинг: 5.0/1
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]