2011-06-17 19 views
5

我只是想知道爲什麼我應該在類中使用屬​​性而不是「正常」變量(類屬性?)。我的意思是這樣的:爲什麼要在一個類中使用屬​​性?

TSampleClass = class 
    public 
    SomeInfo: integer; 
end; 

TPropertyClass = class 
    private 
    fSomeInfo: integer; 
    public 
    property SomeInfo: integer read fSomeInfo write fSomeInfo; 
end; 

最大的區別是什麼?我知道我可以分別定義獲取或保存屬性的getter和setter方法,但即使沒有變量是「屬性」,也可以這樣做。

我試圖尋找爲什麼要使用它,但沒有什麼有用的,所以我在這裏問。

謝謝

+0

Magicmaster - 我必須在這裏失去一些東西;你能解釋一下如何爲你的示例TSampleClass.SomeInfo定義一個getter和setter,並且當你爲SomeInfo變量賦值或者讀取值時,實際上會調用這些方法嗎? – Stuart

+0

看到這個問題:http://stackoverflow.com/q/3963874/267938 –

回答

7

這僅僅是一個具體案件的一個非常簡單的例子,但是,它仍然是一個很常見的情況。

如果您有一個可視化控件,當您更改變量/屬性時,可能需要重新繪製控件。例如,假設您的控件具有BackgroundColor變量/屬性。

加入這樣一個變量/屬性的最簡單方法是讓它成爲一個公共變量:

TMyControl = class(TCustomControl) 
public 
    BackgroundColor: TColor; 
... 
end; 

而在TMyControl.Paint過程中,你畫使用BackgroundColor值的背景。但是這並不是。因爲如果更改控件實例的BackgroundColor變量,控件不會重新繪製自身。相反,新的背景顏色將不會被使用,直到下次由於某種其他原因控件重新繪製時爲止。

所以,你必須做這樣的:

TMyControl = class(TCustomControl) 
private 
    FBackgroundColor: TColor; 
public 
    function GetBackgroundColor: TColor; 
    procedure SetBackgroundColor(NewColor: TColor); 
... 
end; 

其中

function TMyControl.GetBackgroundColor: TColor; 
begin 
    result := FBackgroundColor; 
end; 

procedure TMyControl.SetBackgroundColor(NewColor: TColor); 
begin 
    if FBackgroundColor <> NewColor then 
    begin 
    FBackgroundColor := NewColor; 
    Invalidate; 
    end; 
end; 

,然後使用該控件的程序員使用MyControl1.GetBackgroundColor獲取的顏色,並使用MyControl1.SetBackgroundColor設置它。尷尬了。

使用屬性,你可以擁有兩全其美。事實上,如果你從視程序員的角度做

TMyControl = class(TCustomControl) 
private 
    FBackgroundColor: TColor; 
    procedure SetBackgroundColor(NewColor: TColor); 
published 
    property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor; 
end; 

... 

procedure TMyControl.SetBackgroundColor(NewColor: TColor); 
begin 
    if FBackgroundColor <> NewColor then 
    begin 
    FBackgroundColor := NewColor; 
    Invalidate; 
    end; 
end; 

然後

  • ,他可以讀取和使用單一標識,MyControl1.BackgroundColor屬性設置背景顏色,以及
  • 控制當他設置時重新塗漆!
+0

我忘了提及一件非常重要的事情。其他一些答案已經觸及它,但我想更詳細地解釋一下:在Object Inspector(OI)中看到的每個屬性都是...屬性!在我上面的例子中,屬性'BackgroundColor'將被列在OI中,所以你可以在設計時改變控件的外觀。而當您更改該值時,即使在設計時,也會執行相應的代碼以重新繪製控件!如果你沒有使用屬性,但只有一個'GetBackgroundColor'函數和一個'SetBackgroundColor'過程,OI不會知道...... –

+0

...控件有一個名爲'BackgroundColor'的屬性,它應該使用函數'GetBackgroundColor'來讀取它並使用過程'SetBackgroundColor'來設置它。這是非常重要的一點,我相信你會很感激!事實上,你可能每天都在使用屬性。例如,當你改變'TButton'的Caption'時,你正在使用'TButton'實例的Caption'屬性。這可能是從一個字段變量中讀取並具有setter函數,以便在您鍵入時重新繪製該控件(實際上,Caption屬性不是字符串,而是TCaption ...)。 –

+0

...它是由'type TCaption = type string'定義的,所以它是一個字符串,但是是一個不同的類型,對於這種類型,OI知道它應該在每次更改OI中的值時使用setter,而不僅僅是當你按回車,但那是一個旁註。) –

4

有現實生活的優勢:

    可改爲讀取
  • 屬性/寫/ read'n'write容易,而不需要有獨立的getter和setter都費盡口舌在代碼上;
  • 通過在初始化部分添加一行,可以在子類中公開/發佈屬性;
  • 當涉及到設置字段時,屬性更加友好,比較「Label.Font.SetSize(14)」和「Label.Font.Size:= 14」,您可以將「:=」與製表符/空格和代碼對齊將會更可讀;

編輯:我想到的另一件事,屬性強制你限制獲取/設置方法只有1參數,這對於OOP是好的。與此相比,一些過度設計功能:

GetItem(Index:integer; ForcedIndex:boolean=false):TItem //Forced index to get any value 
GetItem(Index:integer; out Res:PItem):boolean //Result signals if out pointer is valid 
2

這僅僅是一個良好的編程習慣與外界隔離類的非常「內臟」。另外,有關已發佈屬性的信息會存儲到爲該類生成的RTTI中,並且可以通過其名稱,枚舉等進行訪問。例如,在從序列化資源表單讀取表單時使用此功能。

0

你不能監視沒有屬性的變量的變化。

你的讀/寫財產不必須是一個變量,他們可以是功能。然後你可以管理一個屬性的「onChange」。

TmyChange = procedure(Sender: Tobject) of object; 


private 
Fchange : TmyChange; 

public 
property SomeInfo: integer read getFoo write setFoo; 
property onChange : TmyChange read Fchange write Fchange; 

function getFoo : integer 
begin 
    return localFoo; 
end; 

function setFoo (value : integer) 
begin 
    // validate incoming value 
    localFoo=value; 
    if assigned(Fchange) then Fchange(self); 
end; 
+1

我發現了一個bug:在Delphi中,沒有'return'語句。 –

+1

我發現了另一個bug:一個函數需要一個返回值。 –

+0

除非函數是一個對象的方法,否則沒有'Self' ... –

3

我知道我可以定義獲取或分別保存屬性getter和setter方法,但是這是可能的,即使不變量是一個「屬性」。

那麼,沒有。 Setter和getters只是一種普通的方法,只有當它們被用作屬性的讀寫成員時纔會被調用。沒有財產意味着沒有一個吸氣者或二傳手,即使他們是這樣命名的。此外; setters和getters通常被聲明爲private或protected。因此,當您使用公共字段而不是使用公共屬性時能夠調用它們,則需要將這些方法移至公共部分。

此外,字段和屬性之間的巨大差異在於能夠發佈,因此可以在對象檢查器中使用。字段(其他類型,然後是類或接口)不能聲明爲已發佈。

屬性也可以是非常重要的 - 或有用 - 繼承。從技術上講,你不能覆蓋屬性,但你可以用幾種方式模仿覆蓋。一些例子,其中屬性名稱可以從TDescendant被調用,每一個都有自己的目的:

1)抽象:

TBase = class(TObject) 
protected 
    function GetName: String; virtual; abstract; 
    procedure SetName(const Value: String); virtual; abstract; 
public 
    property Name: String read GetName write SetName; 
end; 

TDescendant = class(TBase) 
private 
    FName: String; 
protected 
    function GetName: String; override; 
    procedure SetName(const Value: String); override; 
end; 

2a)的保護(如克羅姆提到,):

TBase = class(TObject) 
private 
    FName: String; 
    function GetName: String; 
    procedure SetName(const Value: String); 
protected 
    property Name: String read GetName write SetName; 
end; 

TDescendant = class(TBase) 
public 
    property Name; 
end; 

2B )

TBase = class(TObject) 
private 
    FName: String; 
protected 
    function GetName: String; 
    procedure SetName(const Value: String); 
end; 

TDescendant = class(TBase) 
public 
    property Name: String read GetName write SetName; 
end; 

通過以上組合,您可以將chang e後代類屬性的行爲。

2

使用屬性的主要原因之一(不管它是否更多OO)是對輸入的驗證,例如,如果您需要將僱員類的年齡限制在像18這樣的有效範圍內。40

TEmp = class 
    private 
    FName: string; 
    FAge: Integer; 
    procedure SetAge(const Value: Integer); 
    procedure SetName(const Value: string); 
    published 
    property Name:string read FName write SetName; 
    property Age:Integer read FAge write SetAge; 
    end; 

..... 

procedure TEmp.SetAge(const Value: Integer); 
begin 
    if not (Value in [18..40]) then 
    raise Exception.Create('Age must be between 18 and 40') 
    else 
    FAge := Value; 
end; 
+0

在你的例子中,驗證是你的setter的一部分,你不需要一個屬性來擁有setter,你可以讓你的setter公開(只有當你真的需要你的setter被髮布時纔有效) – siwmas

相關問題