记录可以用作对象的属性吗?

时间:2012-02-06 06:32:52

标签: delphi properties delphi-xe2

我想将记录作为对象的属性。问题是,当我更改此记录的某个字段时,该对象不知道更改。

type
  TMyRecord = record
    SomeField: Integer;
  end;

  TMyObject = class(TObject)
  private
    FSomeRecord: TMyRecord;
    procedure SetSomeRecord(const Value: TMyRecord);
  public
    property SomeRecord: TMyRecord read FSomeRecord write SetSomeRecord;
  end;

如果我这样做......

MyObject.SomeRecord.SomeField:= 5;

......将无效。

那么当写入其中一个记录的字段时,如何使属性设置过程'catch'?也许在如何申报记录方面有一些技巧?

更多信息

我的目标是避免使用TObject事件(例如TPersistentOnChange)创建TFontTStringList。我不熟悉为此使用对象,但是为了尝试清理我的代码,我看到我是否可以使用Record。我只需要确保在设置其中一个记录字段时可以正确调用我的记录属性设置器。

6 个答案:

答案 0 :(得分:11)

考虑这一行:

MyObject.SomeRecord.SomeField := NewValue;

这实际上是编译错误:

  

[DCC错误]:E2064左侧无法分配到

您的实际代码可能是这样的:

MyRecord := MyObject.SomeRecord;
MyRecord.SomeField := NewValue;

这里发生的是您将记录类型的复制到本地变量MyRecord。然后,您可以修改此本地副本的字段。这不会修改MyObject中保存的记录。为此,您需要调用属性设置器。

MyRecord := MyObject.SomeRecord;
MyRecord.SomeField := NewValue;
MyObject.SomeRecord := MyRecord;

或切换到使用引用类型,即类,而不是记录。

总而言之,您当前代码的问题是没有调用SetSomeRecord,而是您正在修改记录的副本。这是因为记录是值类型,而不是引用类型

答案 1 :(得分:11)

最终,您将需要访问记录的字段,但正如您所建议的那样,记录通常是类中适合的抽象选择。类可以整齐地访问记录的属性,如下所示:

type
  TMyRec = record
    SomeRecInteger: integer;
    SomeRecString: string;
  end;

  TMyClass = class(TObject)
  private
    FMyRec: TMyRec;
    procedure SetSomeString(const AString: string);
  public
    property SomeInteger: integer read FMyRec.SomeRecInteger write FMyRec.SomeRecInteger;
    property SomeString: string read FMyRec.SomeRecString write SetSomeString;
  end;

procedure TMyClass.SetSomeRecString(const AString: string);
begin
  If AString <> SomeString then
  begin
    // do something special if SomeRecString property is set
    FMyRec.SomeRecString := AString;
  end;
end;

注意:

  1. 直接访问记录属性SomeRecInteger
  2. 使用SetSomeRecString仅对此字段执行一些特殊处理。
  3. 希望这有帮助。

答案 2 :(得分:4)

如何使用TObject而不是Record?

type
  TMyProperties = class(TObject)
    SomeField: Integer;
  end;

  TMyObject = class(TObject)
  private
    FMyProperties: TMyProperties;     
  public
    constructor Create;
    destructor Destroy; override;

    property MyProperties: TMyRecord read FMyProperties;
  end;

implementation

constructor TMyObject.Create;
begin
  FMyProperties := TMyProperties.Create;
end;

destructor TMyObject.Destroy;
begin
  FMyProperties.Free;
end;

您现在可以读取和设置TMyProperties的属性,如下所示:

MyObject.MyProperties.SomeField := 1;
x := MyObject.MyProperties.SomeField;

使用此方法,您无需单独获取/设置记录的值。如果需要捕获FMyProperties中的更改,可以在属性声明中添加“set”过程。

答案 3 :(得分:4)

为什么不让记录器的setter / getter成为一部分呢?

TMyRecord = record
  fFirstname: string;
  procedure SetFirstName(AValue: String);
property
  Firstname : string read fFirstname write SetFirstName;
end;

TMyClass = class(TObject)
  MyRecord : TMyRecord;
end;

procedure TMyRecord.SetFirstName(AValue: String);
begin
  // do extra checking here
  fFirstname := AValue;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  MyClass: TMyClass;
begin
  MyClass := TMyClass.Create;
  try
    MyClass.MyRecord.Firstname := 'John';
    showmessage(MyClass.MyRecord.Firstname);
  finally
    MyClass.Free;
  end;
end;

答案 4 :(得分:3)

您按值传递记录,因此对象存储记录的副本。从那时起,实际上有两个对象;原始的和对象持有的副本。改变一个不会改变另一个。

您需要通过引用传递记录。

type
  TMyRecord = record
    SomeField: Integer;
  end;
  PMyRecord = ^TMyRecord;

  TMyObject = class(TObject)
  private
    FSomeRecord: PMyRecord;
    procedure SetSomeRecord(const Value: PMyRecord);
  public
    property SomeRecord: PMyRecord read FSomeRecord write SetSomeRecord;
  end;

答案 5 :(得分:1)

这是@ SourceMaid的答案的替代品。

您可以在对象中使用记录(不是指向记录的指针),并且具有只读属性,该属性返回指向记录的指针。

班级:

type
  TMyRecord = record
    I:Integer;
  end;
  PMyRecord = ^TMyRecord;
  TMyObject = class
  private
    FMyRecord:TMyRecord;
  function GetMyRecordPointer: PMyRecord;
  public
    property MyRecord: PMyRecord read GetMyRecordPointer;
  end;
获取者:

function TMyObject.GetMyRecordPointer: PMyRecord;
begin
  result := @FMyRecord;
end;

用法:

o := TMyObject.Create;
o.MyRecord.I := 42;
ShowMessage(o.MyRecord.I.ToString);
o.MyRecord.I := 23;
ShowMessage(o.MyRecord.I.ToString);
o.Free;

你不需要一个二传手,因为你得到一个参考和使用。这意味着您无法通过指定新记录来更改整个记录 但您可以直接操作记录的元素而不会收到错误"Left side cannot be assigned to"