Pass different record types as parameter in a procedure?

delphi, delphi-xe7, record

Solution

Record instances don't contain type information in the same way that classes do. So you would need to pass an extra argument to indicate which type you were working with. For instance:

type
  TRecordType = (rtPerson, rtSpider);

procedure DoSomething(RecordType: TRecordType; const ARecord);
begin
  case RecordType of
  rtPerson:
    DoSomethingWithThisPerson(TPerson(ARecord));
  rtSpider:
    DoSomethingWithThisSpider(TSpider(ARecord));
  end;
end;

You might contemplate putting the type code in the first field of each record:

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;

You could use generics:

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;

And call the functions like this:

TMyRecordDispatcher.DoSomething(APerson);
TMyRecordDispatcher.DoSomething(ASpider);

This uses generic type inference and so allows you not to explicitly state the type. Although as an example of generics it makes me cringe. Please don't do this.

In my view all of this is messy and brittle. Much of the above reimplements run time method dispatch, polymorphism. Classes are more suited to this. I don't endorse any of the code above.

On the other hand, perhaps this is all needless. What's wrong with:

DoSomethingWithThisPerson(Person);
DoSomethingWithThisSpider(Spider);

Since you know the types at compile time, why opt for anything more complex?

You could use function overloading to make it possible to omit the type from the function name.

procedure DoSomething(const APerson: TPerson); overload;
begin
  ....
end;

procedure DoSomething(const ASpider: TSpider); overload;
begin
  ....
end;

....

DoSomething(Person);
DoSomething(Spider);

Problem

Is there a trick to pass records with different type as parameter in a procedure? For example, look at this pseudo-code: ``` 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; ```

Original source