2009-03-06 21 views
16
でレコードタイププロパティに「左サイドを割り当てることができません」私は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; 

Iのいずれかに値を代入しようとした場合レックプロパティのメンバーは、私はエラー「左サイドを割り当てることができません」取得します:

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

基になるフィールドと同じことをやってすることは許可されている間:

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

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

よろしく

答えて

27

「RECは」プロパティですので、それは最初のプロパティの供述の「読み」を評価する必要があるため、コンパイラは少し違った扱い。あなたの例と意味的に等価である、このことを考えてみます(「」ドット前)

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

あなたはこのようにそれを見れば、あなたは「REC」への最初の参照ことを確認することができ、GETRECを呼び出す必要がありますこれにより、Recの一時的なローカルコピーが作成されます。これらの一時ファイルは、設計上、「読み取り専用」です。これがあなたが走っていることです。あなたがここに行うことができます

もう一つは含むクラスのプロパティとしてレコードの個々のフィールドを抜け出すことです:

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

これは、あなたが直接その組み込みの分野にプロパティを使用して割り当てることができますクラスインスタンスに記録する。

+1

1のようなものを持っているということです! –

4

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

(注:オブジェクトのレコードを変換する場合、結果は実際にはポインタになり、したがってvarパラメータに相当します)。

レコードを保持するには、中間変数(またはField変数)を使用するか、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; 
19

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

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 
    { Private declarations } 
    FRec : TRec; 
public 
    { Public declarations } 
    property Rec : TRec read FRec write FRec; 
end; 

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

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

+3

+1あなたの解決策は悪くないが、そのプロパティのプロパティを "property Rec:TRec read GetRec write FRec;"に変更すると、割り当てトリックは悲惨に失敗する(GetRecは*コピー*はレコードとして*値型*)。 –

+0

TForm1のRecプロパティは、レコードのプロパティへの読み取り/書き込みアクセスのみが必要な場合にのみ読み取ることができます。このソリューションの重要な部分は、レコードのプロパティのセッターメソッドです。 – Griffyn

8

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

2

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

別のオプションのようなものである。このようなことがまだ動作しますので、あなたのためのPRECポインタデリファレンス

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; 

デルファイます:

Form1.Rec.A := 1234; 

の書き込み一部の必要はありませんFRecが指し示すPRecバッファを入れ替える必要がある場合を除き、私は本当にとにかくプロパティを介してそのような交換を行うことを提案しません。

2

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

procedure TForm1.DoSomething(ARec: TRec); 
begin 
    with Rec do 
    A := ARec.A; 
end; 
+0

私はあなたが正しいと思う - レコードのためのプロパティを使用する点はない、それは多くの仕事のように思える...レコードに何かをする手順がある:SetSomething(var ARec:TRec) – sergeantKK

3

プロパティが実際に関数として遵守されているためです。プロパティは値を返すか設定します。それはとても、レコード

への参照やポインタではありません。

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

は次のように関数を呼び出すのと同じです:

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

あなたが何ができるかをされています

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

これはちょっと面倒ですが、このタイプのアーキテクチャには固有のものです。

7

よく使う解決策は、プロパティをレコードへのポインタとして宣言することです。これにより

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は予想通りFRec分野MyRecのすべての値をコピーすることによって動作します。ここ

唯一の落とし穴は、あなたが実際に動作するようにレコードのコピーを取得したいとき、あなたはあなたの答えの後、この4年間にぶつかったMyRec := Form1.Rec^

+0

優れたプロ – Vassilis