Форум: "Основная";
Текущий архив: 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.006 c