Передать различные типы записей в качестве параметра в процедуре?
Есть ли хитрость для передачи записей с другим типом в качестве параметра в процедуре? Например, посмотрите на этот псевдокод:
type
TPerson = record
Species: string;
CountLegs: Integer;
end;
TSpider = record
Species: string;
CountLegs: Integer;
Color: TColor;
end;
var
APerson: TPerson;
ASpider: TSpider;
// Is there a trick to pass different record types as parameter in a procedure?:
procedure DoSomethingWithARecord(const ARecord: TAbstractRecord?);
begin
if ARecord is TPerson then
DoSomethingWithThisPerson(ARecord as TPerson)
else if ARecord is TSpider then
DoSomethingWithThisSpider(ARecord as TSpider);
end;
procedure DefineRecords;
begin
APerson.Species := 'Human';
APerson.CountLegs := 2;
ASpider.Species := 'Insect';
ASpider.CountLegs := 8;
ASpider.Color := clBtnFace;
DoSomethingWithARecord(APerson);
DoSomethingWithARecord(ASpider);
end;
1 ответ
Экземпляры записей не содержат информацию о типе так же, как классы. Поэтому вам нужно будет передать дополнительный аргумент, чтобы указать, с каким типом вы работаете. Например:
type
TRecordType = (rtPerson, rtSpider);
procedure DoSomething(RecordType: TRecordType; const ARecord);
begin
case RecordType of
rtPerson:
DoSomethingWithThisPerson(TPerson(ARecord));
rtSpider:
DoSomethingWithThisSpider(TSpider(ARecord));
end;
end;
Вы можете подумать о том, чтобы поместить код типа в первое поле каждой записи:
type
TPerson = record
RecordType: TRecordType;
Species: string;
CountLegs: Integer;
end;
TSpider = record
RecordType: TRecordType;
Species: string;
CountLegs: Integer;
Color: TColor;
end;
function GetRecordType(ARecord): TRecordType;
begin
Result := TRecordType(ARecord);
end;
....
procedure DoSomething(const ARecord);
begin
case GetRecordType(ARecord) of
rtPerson:
DoSomethingWithThisPerson(TPerson(ARecord));
rtSpider:
DoSomethingWithThisSpider(TSpider(ARecord));
end;
end;
Вы можете использовать дженерики:
type
TMyRecordDispatcher = record
class procedure DoSomething<T: record>(const Value: T); static;
end;
class procedure TMyRecordDispatcher.DoSomething<T>(const Value: T);
begin
if TypeInfo(T) = TypeInfo(TPerson) then
DoSomethingWithThisPerson(PPerson(@Value)^)
else if TypeInfo(T) = TypeInfo(TSpider) then
DoSomethingWithThisSpider(PSpider(@Value)^);
end;
И вызвать функции следующим образом:
TMyRecordDispatcher.DoSomething(APerson);
TMyRecordDispatcher.DoSomething(ASpider);
Это использует вывод общего типа и позволяет вам не указывать тип явно. Хотя в качестве примера дженериков это заставляет меня съеживаться. Пожалуйста, не делай этого.
На мой взгляд, все это грязно и хрупко. Многое из вышеперечисленных переопределений во время выполнения метода отправки, полиморфизм. Классы больше подходят для этого. Я не поддерживаю любой из кода выше.
С другой стороны, возможно, это все излишне. Что случилось с:
DoSomethingWithThisPerson(Person);
DoSomethingWithThisSpider(Spider);
Поскольку вы знаете типы во время компиляции, зачем выбирать что-то более сложное?
Вы можете использовать перегрузку функций, чтобы можно было опустить тип в имени функции.
procedure DoSomething(const APerson: TPerson); overload;
begin
....
end;
procedure DoSomething(const ASpider: TSpider); overload;
begin
....
end;
....
DoSomething(Person);
DoSomething(Spider);