23

Delphi がレコード タイプのプロパティを読み取り専用として扱う理由を知りたいです。

  TRec = record
    A : integer;
    B : string;
  end;

  TForm1 = class(TForm)
  private
    FRec : TRec;
  public
    procedure DoSomething(ARec: TRec);
    property Rec : TRec read FRec write FRec;
  end;

Rec プロパティのいずれかのメンバーに値を代入しようとすると、「左側を代入できません」というエラーが表示されます。

procedure TForm1.DoSomething(ARec: TRec);
begin
  Rec.A := ARec.A;
end;

基になるフィールドで同じことを行うことは許可されています。

procedure TForm1.DoSomething(ARec: TRec);
begin
  FRec.A := ARec.A;
end;

その行動について何か説明はありますか?

4

8 に答える 8

39

「Rec」はプロパティであるため、コンパイラはプロパティ decl の「読み取り」を最初に評価する必要があるため、少し異なる方法で扱います。これは、意味的にあなたの例と同等です。

...
property Rec: TRec read GetRec write FRec;
...

このように見ると、「Rec」への最初の参照 (ドット「.」の前) で GetRec を呼び出す必要があることがわかります。これにより、Rec の一時的なローカル コピーが作成されます。これらの一時ファイルは、設計上 "読み取り専用" です。これはあなたが遭遇しているものです。

ここでできるもう 1 つのことは、レコードの個々のフィールドを、含まれているクラスのプロパティとして分割することです。

...
property RecField: Integer read FRec.A write FRec.A;
...

これにより、プロパティを介して、クラス インスタンスに埋め込まれたレコードのフィールドに直接割り当てることができます。

于 2009-03-06T22:11:20.363 に答える
22

はい、これは問題です。ただし、この問題はレコードプロパティを使用して解決できます。

type
  TRec = record
  private
    FA : integer;
    FB : string;
    procedure SetA(const Value: Integer);
    procedure SetB(const Value: string);
  public
    property A: Integer read FA write SetA;
    property B: string read FB write SetB;
  end;

procedure TRec.SetA(const Value: Integer);
begin
  FA := Value;
end;

procedure TRec.SetB(const Value: string);
begin
  FB := Value;
end;

TForm1 = class(TForm)
  Button1: TButton;
  procedure Button1Click(Sender: TObject);
private
  FRec : TRec;
public
  property Rec : TRec read FRec write FRec;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Rec.A := 21;
  Rec.B := 'Hi';
end;

これはコンパイルされ、問題なく動作します。

于 2009-03-06T23:22:29.070 に答える
11

私が頻繁に使用する解決策は、プロパティをレコードへのポインターとして宣言することです。

type
  PRec = ^TRec;
  TRec = record
    A : integer;
    B : string;
  end;

  TForm1 = class(TForm)
  private
    FRec : TRec;

    function GetRec: PRec;
    procedure SetRec(Value: PRec);
  public
    property Rec : PRec read GetRec write SetRec; 
  end;

implementation

function TForm1.GetRec: PRec;
begin
  Result := @FRec;
end;  

procedure TForm1.SetRec(Value: PRec);
begin
  FRec := Value^;
end;

これにより、直接割り当てForm1.Rec.A := MyIntegerが機能しますが、期待どおりにすべての値をフィールドにコピーすることによっても機能しForm1.Rec := MyRecます。MyRecFRec

ここでの唯一の落とし穴は、実際にレコードのコピーを取得して作業したい場合、次のようにする必要があることです。MyRec := Form1.Rec^

于 2014-05-05T09:04:12.900 に答える
8

コンパイラは、一時への割り当てを停止しています。C# で同等のものを使用できますが、効果はありません。Rec プロパティの戻り値は基になるフィールドのコピーであり、コピーのフィールドへの代入は nop です。

于 2009-03-07T07:25:32.980 に答える
4

暗黙のgetter関数とsetter関数があり、constパラメーターであるため、関数の結果を変更できないためです。

(注:オブジェクト内のレコードを変換する場合、結果は実際にはポインターになるため、varパラメーターと同等になります)。

レコードを保持する場合は、中間変数(またはフィールド変数)を使用するか、WITHステートメントを使用する必要があります。

明示的なgetter関数とsetter関数を使用して、次のコードのさまざまな動作を確認してください。

type
  TRec = record
    A: Integer;
    B: string;
  end;

  TForm2 = class(TForm)
  private
    FRec : TRec;
    FRec2: TRec;
    procedure SetRec2(const Value: TRec);
    function GetRec2: TRec;
  public
    procedure DoSomething(ARec: TRec);
    property Rec: TRec read FRec write FRec;
    property Rec2: TRec  read GetRec2 write SetRec2;
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

{ TForm2 }

procedure TForm2.DoSomething(ARec: TRec);
var
  LocalRec: TRec;
begin
  // copy in a local variable
  LocalRec := Rec2;
  LocalRec.A := Arec.A; // works

  // try to modify the Result of a function (a const) => NOT ALLOWED
  Rec2.A := Arec.A; // compiler refused!

  with Rec do
    A := ARec.A; // works with original property and with!
end;

function TForm2.GetRec2: TRec;
begin
  Result:=FRec2;
end;

procedure TForm2.SetRec2(const Value: TRec);
begin
  FRec2 := Value;
end;
于 2009-03-06T22:29:35.767 に答える
3

これは、プロパティが実際には関数としてコンパイルされているためです。プロパティは、値を返すか設定するだけです。レコードへの参照またはポインタではありません

それで :

Testing.TestRecord.I := 10;  // error

次のような関数を呼び出すのと同じです。

Testing.getTestRecord().I := 10;   //error (i think)

あなたができることは次のとおりです。

r := Testing.TestRecord;    // read
r.I := 10;
Testing.TestRecord := r;    //write

少し面倒ですが、このタイプのアーキテクチャに固有のものです。

于 2012-09-18T05:59:21.253 に答える
2

他の人が言ったように、読み取りプロパティはレコードのコピーを返すため、フィールドの割り当ては TForm1 が所有するコピーには作用しません。

別のオプションは次のようなものです。

  TRec = record
    A : integer;
    B : string;
  end;
  PRec = ^TRec;

  TForm1 = class(TForm)
  private
    FRec : PRec;
  public
    constructor Create;
    destructor Destroy; override;

    procedure DoSomething(ARec: TRec);
    property Rec : PRec read FRec; 
  end;

constructor TForm1.Create;
begin
  inherited;
  FRec := AllocMem(sizeof(TRec));
end;

destructor TForm1.Destroy;
begin
  FreeMem(FRec);

  inherited;
end;

Delphi は PRec ポインタを逆参照するので、次のようなことは引き続き機能します。

Form1.Rec.A := 1234; 

FRec が指す PRec バッファーを交換する場合を除き、プロパティの書き込み部分は必要ありません。とにかく、プロパティを介してそのようなスワッピングを行うことは本当にお勧めしません。

于 2009-03-08T04:29:42.740 に答える
2

最も簡単な方法は次のとおりです。

procedure TForm1.DoSomething(ARec: TRec);
begin
  with Rec do
    A := ARec.A;
end;
于 2010-11-09T21:41:24.397 に答える