程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi中記錄體做為屬性的賦值方法,delphi賦值

Delphi中記錄體做為屬性的賦值方法,delphi賦值

編輯:Delphi

Delphi中記錄體做為屬性的賦值方法,delphi賦值


1. 起源

此問題源於[秋風人事檔案管理系統]用Delphi XE重編譯中所發現。

快十年了,當初Delphi 7所編寫項目,想用Delphi XE重新編譯,並打算做為Free軟件發布,編譯錯誤中發現此問題,感覺頗有些意思,遂記錄下來,以做備忘。

自Delphi 2009之後,轉做c#之WinForm界面開發,Delphi 2010之後未實際做過項目,因此至此才遇到此問題。

此時Delphi XE快快已至XE10版。因情結而不願XE2之後的use方法,遂決定以Delphi XE做為工具,重整老項目。

而此問題,簡而言之,就是當記錄體(record)做為屬性出現時,其賦值問題。

 

2. 賦值

比如,我有一記錄體屬性如下(這裡以TPoint說明問題,實際項目中為自定義記錄體):

type
  TForm1 = class(TForm)
    btnTest: TButton;
    procedure btnTestClick(Sender: TObject);
  private
    { Private declarations }
    FPoint: TPoint;
  public
    { Public declarations }
    property Point: TPoint read FPoint write FPoint;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnTestClick(Sender: TObject);
begin
  //Point.X := 12; //如此賦值不成功,報錯為Left side cannot be assigned to
  //Point.Y := 12;
  with Point do
  begin
    X := 12;
    Y := 12;
  end;
  ShowMessage(Format('x: %d, y: %d', [Point.X, Point.Y]));
end;

直接賦於做為屬性的record值,比如Point.X := 12,編譯都不能通過。我們可以理解為面向對象封裝問題不允許操作其內部數據,因為記錄體為值類型。

但事有折中,借with語句,即可方便賦值。所以在以往項目中,多用此寫法,簡潔而方便,而這種寫法在Delphi 2009及之前版本,都支持,可能是Delphi編譯器的BUG吧!

如果是,我喜歡這個BUG,它簡潔了我的寫法,特別是屬性有多級嵌套時。

 

3. 不再支持

Delphi命運多舛,像個不能決定自己命運的小姑娘一樣被賣來賣去。工作原因換了開發工具,十多年的Delphi開發從此擱置,不曾想其間多少變故,如今再用,細微變化已別於昔時。

如上面代碼,用with語句賦值,也行不通了,報錯為[DCC Error] Unit1.pas(34): E2064 Left side cannot be assigned to.

baidu看有沒有人類似疑惑?可能國內用Delphi漸少原因吧,竟沒找到類似問題,於是stackoverflow一下,果然碰到一根筋的難兄難弟:

Left side cannot be assigned for a record type

Delphi “E2064 Left side cannot be assigned to” error appeared when upgrading a project from 2009 to XE

Delphi 2010+ and “Left side cannot be assigned to” in read-only records: can this be disabled?

看諸回復,大意是Delphi 2010及以後版本其編譯器檢查比以前更為嚴格,其中還有人感謝這樣更改。討論比較熱烈,說啥的都有。日!

與提問者一樣,我只想簡單地做為屬性以記錄需的的數據,僅此而已。這是一個完全有效的語言特性,就這樣給干了,這是歷史的退步!

 

4. 解決方法

這條路關閉了,總還有其它路,是不是?編譯器想必真是處理屬性為Get、Set方式,而加以特別處理?

總結下吧!如果還想用此方法,就折中下:

a. 直接換屬性為字段,即

property Point: TPoint read FPoint write FPoint;

換為Point: TPoint;

這條方法違背於面向對象封裝,不好,但能用。

b. 若是自定義record,可換為class,這思路中。

c. 以指針方式去賦值:

procedure TForm1.btnTestClick(Sender: TObject);
begin
  with PPoint(@Point)^ do
  begin
    X := 12;
    Y := 12;
  end;
  ShowMessage(Format('x: %d, y: %d', [Point.X, Point.Y]));
end;

我個人傾向於這個。自定義記錄體,再定義個指向它的指針結構,就可以了。

d. 以臨時變量代之,反賦回去:

procedure TForm1.btnTestClick(Sender: TObject);
var
  p: TPoint;
begin
  with p do
  begin
    X := 12;
    Y := 12;
  end;
  Point := p;
  ShowMessage(Format('x: %d, y: %d', [Point.X, Point.Y]));
end;

這應該是正統寫法,但總感覺繁瑣令人難受。

反正是少了之前版本那灑脫淋漓簡潔隨意的寫法,蛋疼之極也!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved