Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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]

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



Страницы: 1 вся ветка

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

Наверх





Память: 0.55 MB
Время: 0.005 c
4-94609
Begunkov
2002-02-21 20:00
2002.04.29
Блокировка части файла


4-94614
ebeden
2002-02-23 01:51
2002.04.29
Перехват нажатий клавиш


3-94267
Viktor Erko
2002-04-08 08:49
2002.04.29
Индексы в отдельном каталоге


6-94524
Юра
2002-02-16 18:11
2002.04.29
Учет трафика Инета в локалке (WinProxy) для каждого компа


1-94433
dnk
2002-04-15 19:01
2002.04.29
FreeReport.frf -> FastReport.frf





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский