Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.04.29;
Скачать: [xml.tar.bz2];




Вниз

Как описать входной параметр процедуры многовариантно? 


lipskiy   (2002-04-05 21:43) [0]

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

Может быть есть все-таки вариант описания входного параметра в виде списка возможных типов?
Конкретно мне нужно следующее:
proceudre Some(Input:String);
proceudre Some(Input:TStringList);
Параметр Input мне нужно описать так, чтобы при вызове ОДНОЙ процедуры Some можно было задать или String, или TStringList.
Как это сделать?



VID   (2002-04-06 01:00) [1]

а ты пробовал использовать тип VARIANT, а потом проводить проверку типа переменной, или ещё что-то в этом роде ?



lipskiy   (2002-04-06 01:26) [2]

Не-а, не пробовал.
Я это не умею - с variant, я его боюсь, он какой-то очень виртуальный.
Шутка.
Как это делается, ась?
Если мона - примерчик.



Вася Пупкин   (2002-04-06 01:31) [3]

Есть еще возможность (Delphi >=4)
В рамках одного класса можно иметь несколько одноименных методов.При обнаружении одноименного метода компилятор предупреждает, что у класса уже есть аналогичный метод с другими параметрами. Для подавления сообщений объявление метода можно сопровождать словом reintroduce.
Чтобы одноименные методы можно было отличить друг от друга, каждый из них должен иметь уникальный набор параметров.



lipskiy   (2002-04-06 01:35) [4]

Дык мне не надо так.
Мне надо один метод, но чтоб один из входных параметров мог быть в разных типах.



Вася Пупкин   (2002-04-06 01:44) [5]

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

type
TForm1 = class(TForm)
....
private
procedure Close(S:string);reintroduce;overload;
procedure Close(i:integer);reintroduce;overload;
procedure Close(i,j:integer);reintroduce;overload;

implementation
...
procedure TForm1.Close(S:string);
begin
....
end;

procedure TForm1.Close(i:integer);
begin
....
end;



Вася Пупкин   (2002-04-06 01:46) [6]

Или пользуй VARIANT VID © (06.04.02 01:00)



VID   (2002-04-06 01:46) [7]

можно пойти на изврат для конкретного случая:
Procedure Some (input:TStringList; IsTypeString:Boolean);
//В параметре input передаёшь список
//В параметре IsTypeString ставишь TRUE когда передаёшь строку, иначе - FALSE
begin
IF IsTypeString = true then
begin
Result := Input.Items.Strings[0]; //В этой строке списка и должно находиться нужное строковое значение
end
else
begin
Result := Input.Items.Count //В этом случае процедура работает уже как-бы со списком
end;

end;

спору нет - изврат, но сгодится :)))



VID   (2002-04-06 01:48) [8]

TO Вася Пупкин: Variant не подходит... комплятор ругается :(



Вася Пупкин   (2002-04-06 02:07) [9]

У меня не ругается :)

unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}
procedure Foo(V:variant);
begin
Form1.Label1.Caption:=v;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Foo("String");
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Foo(123);
end;
end.



SPeller   (2002-04-06 02:12) [10]

А если передавать в процедуру один параметр - указатель, а второй - идентификатор на что этот указатель указывает ?? И не надо никаких вариантов ?



Вася Пупкин   (2002-04-06 02:24) [11]

SPeller © (06.04.02 02:12)Хороший вариант :)(сорьки, каламбур получился). Но не всегда годится.



SPeller   (2002-04-06 03:48) [12]

Можно сделать как я уже предлагал. Чтобы передать несколько строк, можно воспользоваться принципом как в ф-ции GetOpenFileName при передаче фильтров. Сделать свою ф-цию, которая выполняет всю эту рутину и передавать ей StringList, а она возвратит то, что надо. Мне кажется, это не так сложно, и выполняться оно будет быстрее, нежели обработка вариантов.



Вася Пупкин   (2002-04-06 05:44) [13]

Да уж...на TStringList как-то не посмотрел...
Варианты не могут быть ссылками на объекты Object Pascal. Поэтому VID © (06.04.02 01:48) "Variant не подходит... комплятор ругается " абсолютно прав.
Так что для случая
proceudre Some(Input:String);
proceudre Some(Input:TStringList);
остается либо писать "извраты" - один из вариантов -SPeller © (06.04.02 02:12)... либо использовать reintroduce ( имхо, это проще...)
Пример использования reintroduce для конкретного случая (немного усложним)
proceudre Some(Input:variant);
proceudre Some(Input:TStringList);



unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TForm1 = class(TForm)
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure Some(V:variant);reintroduce;overload;
procedure Some(V:TStringList);reintroduce;overload;
public
{ Public declarations }
end;

var
Form1: TForm1;
L:TStringList;
implementation

{$R *.DFM}
procedure TForm1.Some(V:variant);
begin
Form1.Label3.Caption:=v
end;
procedure TForm1.Some(v:TStringList);
begin
Form1.Label1.Caption:=v[0];
Form1.Label2.Caption:=v[1];
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
Some("123");
Some(L);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
L:=TStringList.Create;
L.Add("AAA");
L.Add("BBB");
end;

end.



Вася Пупкин   (2002-04-06 06:39) [14]

А то, что два тела - так можно же сделать, например, так :
...
{ Private declarations }
procedure some(V:TStringList);reintroduce;overload;
procedure some(v:String);reintroduce;overload;
public
{ Public declarations }
end;
....
implementation

{$R *.DFM}
procedure TForm1.some(v:TStringList);
begin
// Form1.Label1.Caption:=v[0];
//Что-то делаем
end;
procedure TForm1.some(V:string);
//сделаем из string StringList
var Tmp:TStringList;
begin
Tmp:=TStringList.Create;
Tmp.Add(v);
//И вызовем перегруженную функцию для StringList
some(Tmp);
end;
при этом второе "тело" будет очень небольшим ;)



Вася Пупкин   (2002-04-06 06:50) [15]

либо так (это уж зависит от потребностей):

procedure TForm1.some(v:TStringList);
var i:integer;
begin
for i:=0 to V.Count-1 do some(v[i]);
end;
procedure TForm1.some(V:string);
begin
//Что-то делаем
end;



Fantasist   (2002-04-06 06:56) [16]

Variant - вещь замечательная. Для простых типов. И перегрузка вещь замечательная. Я бы скомбинировал так:


procedure sm(vr:variant); overload;
begin
if TVarData(v).VType=varByRef then
TButton(Pointer(TVarData(vr).VPointer)).Caption:="t"
else
....
end;

procedure sm(vt:TObject); overload;
var
v:variant;
begin
TVarData(v).VType:=varByRef;
TVarData(v).VPointer:=Pointer(vt);
sm(v);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
sm(Sender);
end;


Можно еще в TVarData.Reserved1 добавить служебную информацию, что за класс. А если маленько подумать, то можно сделать переменную типа ClassId:class of TObject и распихать ее по TVarData.Reserved1 и TVarData.Reserved2 и тогда можно вообще красиво анализировать. Типа:
case classId of
TObject:
TButton:
.....



Вася Пупкин   (2002-04-06 07:29) [17]

2Fantasist (06.04.02 06:56)А Вы пробовали это запустить ?
Вся беда в том, что variant не может содержать ссілку на данные типа Pointer или class... боюсь, тут придется несколько больше "извращаться" :(

procedure sm(vt:TObject); overload;
var
v:variant;
begin
TVarData(v).VType:=varByRef;
TVarData(v).VPointer:=Pointer(vt);
sm(v); // здесь получите Error....
end;



Вася Пупкин   (2002-04-06 08:04) [18]

А вот так - пожалуйста :)
...
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
end;

var
Form1: TForm1;

implementation

{$R *.DFM}
procedure sm(vr: array of const);
begin
if vr[0].VType=VtObject then
begin
(vr[0].VObject as TButton).Caption:="t"
end
else
Form1.Label1.Caption:=String(vr[0].VPChar);
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
sm([Sender]);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
sm(["O-o-p-s"]);
end;

end.



Fantasist   (2002-04-06 09:02) [19]


> А Вы пробовали это запустить ?
> Вся беда в том, что variant не может содержать ссілку на
> данные типа Pointer или class... боюсь, тут придется несколько
> больше "извращаться" :(


Вот сейчас попробывал. Работает. Кто вам сказал, что pointer туда записывать нельзя? А на кой тогда ByRef?
В крайем случае, если в какой-то версии это все-таки не поддерживается(со стандартом не сверялся), можно затолкать это как Integer.



Вася Пупкин   (2002-04-06 09:50) [20]

2 Fantasist (06.04.02 09:02)В Delphi 6 действительно будет работать, но в Delphi 5 выдаст "Invalid variant operation"...



Bachin   (2002-04-06 12:04) [21]

2 Вася Пупкин © (06.04.02 09:50)
Делфи 5,6 тут ни при чем!
Почитай как работать с вариантами.
Это пройдет и на Делфи 3 (проверено).

2 All
Если бы точно знать постановку задачи -
можно было бы о чем-то говорить. В большинстве случаев (думаю похожих)
если не надо возвращать в эту переменную параметр отлично подходит
TVarRec.

Типа:
function BindParamArray( HStmt : THStmt;
const BindArray : array of const ) : boolean;
var
Index: integer;
begin
for Index := 0 to High(BindArray) do
begin
case BindArray[Index].VType of
vtInteger: begin
...................
ну и вызов
BindParamArray( ..., [111])
BindParamArray( ..., ["String"])
BindParamArray( ..., [111, "String", 11.25, DateTime])
...................



Вася Пупкин   (2002-04-06 12:37) [22]

>Bachin © (06.04.02 12:04)Я не утверждаю, что код Fantasist (06.04.02 06:56)не будет компилироваться.
Факт - что если этот код ( без изменений) откомпилировать в D5,то работать не будет .



lipskiy   (2002-04-06 13:24) [23]

ВАУ! Ребята! Всю ночь работали :) Спасибо всем!
Но все вроде не совсем то, хотя некоторые мысли надо попробовать, если, конечно проще никак.

Постановка задачи, более конкретно, такая.
Есть процедура, которая скачивает с инета один ресурс.
Этот ресурс может быть скачан и сохранен двумя способами - записан в файл на диск или загружен в StringList как текстовик.

В зависимости от способа сохранения я вызываю процедуру с одним из двух возможных параметров:
Receiver:String;
или
Receiver:TStringList;

В первом случае Receiver содержит имя файла на диске, в который будет записан скачанный файл.
Во втором случае Receiver содержит StringList, в который будут загружены строки скачанного текстового файла.

Есть также компонент закачки, его экземпляр называется HttpGet.
Он закачивает ресурс в свой поток, создание которого я вызываю в этой процедуре.

Тело процедуры должно быть одно, выглядеть она должна примерно так:

procedure DownLoad (Receiver:{что-то многовариантное});
var ToList:boolean;
FName:string; // имя приемника
DataIn : TStream;
begin
// Определяем тип сохранения закачанных данных
ToList:= Receiver is TStringList;
}
некоторый подготовительный код, который не хочется дублировать в двух телах процедур
}
if ToList then FName:= HttpGet.DocName // генерирует сам компонент закачки
else FName:= Receiver; // берется входное значение
// создем поток для закачки
HttpGet.RcvdStream:= TFileStream.Create(FName, fmCreate);
HttpGet.Get; // закачиваем

if ToList then // грузим данные в стринглист
begin
DataIn:= TFileStream.Create(FName, fmOpenRead);
Receiver.LoadFromStream(DataIn);
DataIn.Free;
end;
// иначе данные сохранились в файл с именем Receiver.

HttpGet.RcvdStream.Free;
}
некоторый завершающий код, который не хочется дублировать в двух телах процедур
}
end;


Это не рабочий вариант - писал только тут для объяснения логики.
Ну чего? Мне бы простенько как нибудь воткнуть входной параметр, и все. Если никак, то мне проще объявить два входных параметра и один флажок - какой на самом деле активный, это хоть и некрасиво, но все таки проще предложенных пока вариантов.



MBo   (2002-04-06 17:20) [24]

Procedure DownLoad (Receiver:TStringList;Fname:String="");

вызов 1
Download(nil,"e:\qq.txt");
вызов 2
Download(Stringlist1);

в теле
If Fname="" then receiver заполняем

можно и без default параметра String="", тогда
2) Download(Stringlist1,"");






SPeller   (2002-04-06 17:47) [25]

По-моему, будет проще, легче, логичнее, нагляднее и быстрее сделать так, как предложил MBo © (06.04.02 17:20). Так можно будет и сохранить в файл и одновременно в стринглист, чтобы не вызывать несколько раз. Просто поставить условия в теле процедуры, мол если то-то, то в стринглист, а если то-то, то в файл.



lipskiy   (2002-04-06 21:40) [26]

:)
Я уже ровно так и сделал :)
Но все равно, спасибо.



ivv   (2002-04-12 06:55) [27]

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



REA   (2002-04-12 11:19) [28]

Выражаю солидарность с ivv



PVOzerski   (2002-04-12 11:44) [29]

Резюмирую: плохо в OP без макросов...



SPeller   (2002-04-12 12:13) [30]

ivv © (12.04.02 06:55)
Какой вы продвинутый программист
А если надо оптимизировать на время выполнения, то перегружаемые функции не подойдут. Как и многие здешние варианты.



Бурундук   (2002-04-12 12:20) [31]

PVOzerski ©
cpp esse delendam :)



Fantasist   (2002-04-12 17:39) [32]


> Какой вы продвинутый программист
> А если надо оптимизировать на время выполнения, то перегружаемые
> функции не подойдут. Как и многие здешние варианты.


Как это? Перегружаемые функции и есть самая хорошая оптимизация по времени исполнения - все определяется на момент компиляции.



lipskiy   (2002-04-13 22:33) [33]

Перегружаемые функции требуют описания двух тел. Это неудобно в данном случае и про такой вариант я и сам знаю. Вопрос был именно для одного входного параметра одной процедуры.
У меня большая прецедура, ветвление где-то в середине, и что, мне дублировать ее дважды?
Имхо, лучшее решение, коим я и воспользовался - два входных параметра разного типа, один из которых задан, другой пустой (MBo © (06.04.02 17:20)).



IGOREK   (2002-04-14 15:04) [34]

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



lipskiy   (2002-04-14 19:14) [35]

Да неудобно это!
Вот структура:

begin
// много общего кода

// ветвление в зависимости от входного параметра
if (input is string) then
// много кода по одной ветке
else
// много кода по другой ветке

// и снова много общего кода
end;


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



IGOREK   (2002-04-15 02:30) [36]

Все таки перезагрузка вещь хорошая.
Посмотрел историю обсуждения, и нашел, что тебе надо передавать
то String, то StringList.
Предлагаю такой вариант (может где ошибки - не компилировал):

procedure f1(FileName: TString)
var
SL: TStringList;
begin
SL.Lines.Add(FileName);
f2(SL);
end

procedure f2(var SL: TStringList)
begin
//общий код

if SL.Lines.Count = 0 then
//код для записи в SL
else
//код для записи в файл

//общий код
end

Может так тебе понравится ;-)



lipskiy   (2002-04-16 01:26) [37]

Ладно, ок, понял я все варианты, спасибо.
Этот тоже хорош, но структура моего конкретного случая немного сложнее, поэтому там это менее удобно.
Ладно, ребята, закроем эту ветку, а? :)
Всем спасибо!




Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.04.29;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.82 MB
Время: 0.057 c
3-94276           Glonia Zbanov         2002-04-08 11:19  2002.04.29  
проблемы русского языка


3-94263           Rail                  2002-04-07 16:05  2002.04.29  
Как показать последнюю ошибу при работе с БД через BDE


3-94265           cranium               2002-04-07 20:36  2002.04.29  
Список пользователей базы InterBase


4-94643           Holder                2002-02-27 02:12  2002.04.29  
Помогите пожалуйста


1-94361           ALM                   2002-04-17 12:51  2002.04.29  
Странное дело, однако...