【问题标题】:Record is empty?记录为空?
【发布时间】:2021-04-23 08:16:12
【问题描述】:

Delphi 的记录类型中是否有任何数据?怎么理解?

例如,应该得到这样的记录类型;

type
  TDummy = PACKED record
    Text   : String;
    Number : Integer;
  end;
var
  aRecord : TDummy;
begin
  aRecord := default(TDummy);   // In this state "aRecord" is empty. (Text = '' and Number = 0)
  aRecord.Text := 'TEST';       // This is no longer empty
end;

那么有什么方法可以在不浏览记录项的情况下解决这个问题?

【问题讨论】:

  • "在这种状态下,"aRecord" 是空的" 不,它不是空的。它包含空字符串和数字 0。
  • 是的,你是对的。请问,我怎么知道他是不是真的这样?
  • 最好的方法是声明一个方法TDummy.IsEmpty: Boolean
  • 你能举个简单的例子吗?

标签: class delphi data-structures types record


【解决方案1】:

使用成员函数

到目前为止,最简洁的方法,恕我直言,是声明一个方法TDummy.IsEmpty: Boolean

type
  TDummy = record
    Text: string;
    Number: Integer;
    function IsEmpty: Boolean;
  end;

{ TDummy }

function TDummy.IsEmpty: Boolean;
begin
  Result := (Text = '') and (Number = 0);
end;

那么你可以随时使用这个方法来查看记录是否为空:

procedure TForm1.FormCreate(Sender: TObject);
var
  D: TDummy;
begin

  D := Default(TDummy);

  if D.IsEmpty then
    ShowMessage('D is empty.');

  D.Number := 394;

  if D.IsEmpty then
    ShowMessage('D is empty.');

end;

使用等于运算符

另一种方法:

type
  TDummy = record
    Text: string;
    Number: Integer;
    class operator Equal(const Left, Right: TDummy): Boolean;
    class operator NotEqual(const Left, Right: TDummy): Boolean;
  end;

const
  EmptyDummy: TDummy = ();

{ TDummy }

class operator TDummy.Equal(const Left, Right: TDummy): Boolean;
begin
  Result := (Left.Text = Right.Text) and (Left.Number = Right.Number);
end;

class operator TDummy.NotEqual(const Left, Right: TDummy): Boolean;
begin
  Result := not (Left = Right);
end;

现在你可以做

procedure TForm1.FormCreate(Sender: TObject);
var
  D: TDummy;
begin

  D := Default(TDummy);

  if D = EmptyDummy then
    ShowMessage('D is empty.');

  D.Number := 394;

  if D = EmptyDummy then
    ShowMessage('D is empty.');

end;

疯狂的东西

如果您绝对不想单独比较每个成员,您可以在某些情况下比较字节。

但请注意,一般情况下,您不能通过比较它们的字节来比较两条记录。只提两个明显的原因:

  1. 字符串成员在语义上可能是相等的,即使它们由两个不同的字符串堆对象表示(因此比较器说“不相等”而实际上它们是相等的)。

  2. 如果记录不是packed,则记录可能有填充(因此比较器可能会说“不等于”,而实际上它们是相等的)。

但您只想与“默认”(归零)值进行比较,作为奖励,您的记录类型恰好是 packed,因此您可以侥幸逃脱

type
  TDummy = packed record
    Text: string;
    Number: Integer;
  end;

  TZeroRecord<T: record> = record
    class function IsZero([Ref] const ARecord: T): Boolean; static;
  end;

{ TZeroRecord<T> }

class function TZeroRecord<T>.IsZero([Ref] const ARecord: T): Boolean;
begin
  var DefT := Default(T);
  Result := CompareMem(@ARecord, @DefT, SizeOf(T));
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  D: TDummy;
begin

  D := Default(TDummy);

  if TZeroRecord<TDummy>.IsZero(D) then
    ShowMessage('D is empty.');

  D.Number := 394;

  if TZeroRecord<TDummy>.IsZero(D) then
    ShowMessage('D is empty.');

end;

但这太疯狂了。

【讨论】:

  • @CivatA:是的,但恕我直言,这将是非常过分的,而且速度会慢几个数量级(在大多数情况下,这并不是什么大问题,因为现在的计算机真的很快)。
  • 既然你只比较,我不这么认为。所有空字符串都应该是nil 指针,所有空动态数组、对象引用和接口也是如此。而你只阅读。可能发生的最糟糕的事情是错误的“它不为零”,例如,如果您将来要添加记录 member 并且 it 不会是 packed .
  • 我现在从你那里学到了很多东西。多谢,伙计。我认为这样的陈述可能更简单; ` 函数 TDummy.IsEmpty:布尔值; var DefT: TDummy;开始 DefT := Default(TDummy);结果 := CompareMem(@Self, @DefT, SizeOf(TDummy));结尾; `
  • @CivatA:是的。但是我的TZeroRecord 的重点是,您可以定义该类型 once,然后将其用于所有(现在和未来)打包记录类型(不仅是您今天的 TDummy,还包括您的TFrogTProcyonidae 明天)。 (因此您无需在每次创建新的打包记录类型时都定义IsEmpty 方法。)
猜你喜欢
  • 2019-04-09
  • 1970-01-01
  • 2020-02-25
  • 1970-01-01
  • 1970-01-01
  • 2015-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多