Может ли запись использоваться как свойство объекта? - PullRequest
11 голосов
/ 06 февраля 2012

Я хотел бы сделать запись как свойство объекта. Проблема в том, что когда я изменяю одно из полей этой записи, объект не знает об этом изменении.

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 или TPersistent с событием OnChange (например, TFont или TStringList). Я более чем знаком с использованием объектов для этого, но в попытке очистить мой код немного, я вижу, могу ли я вместо этого использовать запись. Мне просто нужно убедиться, что мой установщик свойств записи может вызываться правильно, когда я устанавливаю одно из полей записи.

Ответы [ 6 ]

11 голосов
/ 06 февраля 2012

В конечном итоге вы захотите получить доступ к полям записи, но, как вы предлагаете, запись часто является подходящим выбором абстракции в классе.Класс может аккуратно обращаться к свойствам записи следующим образом:

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 для реализации специальной обработки только для этого поля.

Надеюсь, это поможет.

11 голосов
/ 06 февраля 2012

Рассмотрим эту строку:

MyObject.SomeRecord.SomeField := NewValue;

На самом деле это ошибка компиляции:

[Ошибка DCC]: E2064 Левая сторона не может быть назначена

Ваш реальный код, вероятно, выглядит примерно так:

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

Здесь происходит то, что вы копируете значение типа записи в локальную переменную MyRecord.Затем вы изменяете поле этой локальной копии.Это не изменяет запись, хранящуюся в MyObject.Для этого вам нужно вызвать установщик свойств.

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

Или переключиться на использование ссылочного типа, то есть класса, а не записи.

Подводя итог, проблема с вашимтекущий код состоит в том, что SetSomeRecord не вызывается, и вместо этого вы изменяете копию записи.И это потому, что запись имеет тип значения , а не тип ссылки .

4 голосов
/ 06 февраля 2012

Почему бы не сделать установщик / получатель частью записи?

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 голосов
/ 06 февраля 2012

Как насчет использования объекта TObject вместо записи?

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 голосов
/ 06 февраля 2012

Вы передаете запись по значению, поэтому копия записи сохраняется объектом. С этого момента есть фактически два объекта; оригинал и копия, хранящаяся в объекте. Изменение одного не изменит другого.

Вам необходимо передать запись по ссылке.

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;
1 голос
/ 07 ноября 2014

это альтернатива ответу @ 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".

...