【问题标题】:Pass different record types as parameter in a procedure?在过程中传递不同的记录类型作为参数?
【发布时间】:2015-02-26 19:12:20
【问题描述】:

在过程中传递不同类型的记录作为参数是否有技巧?例如,看看这个伪代码

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;

【问题讨论】:

  • 记录不支持继承,请阅读精品手册。

标签: delphi record delphi-xe7


【解决方案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);

【讨论】:

  • 哇,这真是又快又巧妙/太棒了!谢谢!
  • 声明类会产生大量代码。然后需要从它们创建对象,然后正确释放。所有这些都需要大量的工作和编码。 RECORDS 使用起来非常容易和简单,没有麻烦,代码也不多。所以在很多情况下我更喜欢记录。
  • 在这种情况下,记录将导致比基于类的替代方案更多的代码和更少的类型安全性。记录与类的区别很大程度上取决于您希望赋值运算符的含义。你想要它的意思是复制还是参考?在我看来,这确实是记录和类的问题。另一件倾向于推动决定的事情是你所面临的。记录不支持多态性。上课。你想要多态性。要么通过类免费获得它,要么在没有编译器安全网的情况下自行实现。
  • 我同意 David Heffernan 关于在您的情况下使用类而不是因为您显示的记录设计确实表明您可能会更好地使用多态性。但是如果你真的想使用记录,你可以去使用方法重载。这意味着您将拥有多个名称相同但参数不同的方法。所有这些都必须跟在 Overload 命令之后。这样,您的程序将根据提供给它的参数自动决定使用哪种方法。
  • 我认为变种是撒旦的产物
猜你喜欢
  • 1970-01-01
  • 2012-07-04
  • 1970-01-01
  • 2015-03-29
  • 2021-02-12
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多