前言
最近在工作的過程中常常會覺得自己在程序設計方面的能力還是有欠缺。例如一直對于變量的聲明感到不足,在工作中為了圖方便總是直接public定義字段,實際上造成了很多困擾,特寫此文總結一下應當怎樣定義成員變量。
屬性和字段的區別
字段
【字段】
- 字段(Field)是一種表示與對象或類關聯的變量的成員,字段聲明用于引入一個或多個給定類型的字段。字段是類內部用的,private類型的變量(字段),通常字段寫法都是加個"_"符號,然后聲明只讀屬性,字段用來儲存數據。
public int Index = 0;
private int m_index;
private static int s_index;
public static int s_Index;
字段,或者說變量是類中最基本的要素。一般來說我們定義一個字段的時候,需要考慮的是以下幾點:
- 使用何種訪問修飾符
- 是否使用其他關鍵字
- 定義的變量類型
- 變量名的取名
- 變量在定義時的賦值
以上要素對于屬性定義也是一樣的。
訪問修飾符和關鍵字定義
這里這介紹一些常用的:
當我們希望一個字段可以被其他類訪問時,通常會將其定義為public
,若不希望被其他類訪問,則為private
,若想要被子類訪問,則會定義為protected
然后是一些關鍵字的運用:若想要即使生成了多個類,但其中的一些字段值依舊全局唯一,我們會使用static
來定義。
如果我們想要對字段值在方法中進行類似指針的賦值引用,會用到ref,in,out
關鍵字,或者直接進行指針引用(指針引用和C相同,但是必須要在unsafe
塊中進行)。
ref,in,out
的共同點是:
- 需要在方法定義與方法調用處顯示的使用關鍵字。
- 參數按引用傳遞,而非值傳遞。
ref,in,out
的不同點是:
ref
:參數變量需要初始化,參數在方法中可以修改或不修改。out
:參數變量無需初始化,參數在方法中必須進行賦值。in
:參數變量需要初始化,參數在方法中不能進行修改。
在類中實現方法的時候,我們還會用到abstract
關鍵字,virutal
和override
關鍵字。abstract
用于抽象類中定義的抽象方法。而virutal
定義了父類虛方法,override
則用于繼承了該父類的子類中重寫同名虛方法和抽象方法。還有sealed
關鍵字用來密封類或者函數,如果類使用了sealed
則不可被繼承,若函數使用了override sealed
來描述則該方法不可再被override
重寫
關鍵字還有很多種,例如定義委托的delegate
,基本語法的if,else,switch,case,break,continue,
等等等等
變量類型的定義
定義的變量類型當然是需要什么定義什么,有些特殊的情況,例如我們在調用class類型的變量時,可以不定義為該class本身的類型:若需要訪問其父類中的方法,則可以直接定義為它的父類;若需要訪問該類中繼承的接口方法,也可以直接定義為它的接口類型。
變量命名
變量名的取名我通常是匈牙利命名法和駝峰命名法相結合:
- 私有成員變量
m_name
,例如m_maxNum
- 公共變量
Name
,例如MaxNum
- 常量或宏
NAME
,例如MAX_NUM
- 靜態變量
s_
開頭,結合公有或私有命名
雖然C#官方推薦私有變量_
開頭,但是似乎許多宏也是_
開頭的,為避免不必要的問題,我覺得不用以_
開頭
從取名上盡量要取得詳細,除了一些專用名詞如TCP,UDP等等,盡量不要使用首字母大寫進行縮寫。例如一個TCP的接收消息管理器我會命名為TCP_ReceiveManager
,或者一個游戲場景內物體管理器GameSceneObjManager
。不要因為嫌麻煩而簡化命名
變量的賦值
一些變量在運行時需要提前進行初始化,否則會導致空引用,而一些變量雖然不賦值也可以調用,但往往會導致錯誤的結果。因此變量初始化的賦值很重要,要么在腳本開始執行時調用一個初始化方法統一分配,要么就直接在字段定義時進行賦值。
屬性
【屬性】
- 屬性(Property)是另一種類型的類成員,定義屬性的目的是在于便于一些私有字段的訪問。類提供給外部調用時用的可以設置或讀取一個值,屬性則是對字段的封裝,將字段和訪問自己字段的方法組合在一起,提供靈活的機制來讀取、編寫或計算私有字段的值。屬性有自己的名稱,并且包含get 訪問器和set 訪問器。
屬性和字段的定義幾乎是一模一樣的,唯一的不同在于屬性提供了get
訪問器和set
訪問器。這使得我們可以在為屬性進行取值和賦值的時候定義一個自定義的方法。
屬性本質上可以看作是字段的一層封裝,它的內部包含了一個私有字段,并提供了一個get和set來讀寫這個私有字段,如下所示:
pulic class User
{private string m_name;//_name為字段public string Name //Name為屬性,它含有代碼塊{get{return m_name;//讀取(返回_name值)}set {m_name= value;//為m_name賦值,value可以直接獲取賦值}}
}
所以為什么我們要多一層封裝而非直接將字段public出來呢?因為程序設計中我們不希望一個字段可以被其他類輕易的訪問,有時我們只想它可讀或者可寫,或者在讀寫時進行一些其他操作。如果不用屬性則需要定義讀寫的方法,顯然太麻煩了。
使用屬性,我們可以用get代表字段可讀,set代表可寫,以此控制字段的讀寫權限。同時屬性接口是只對外的(對內直接修改字段即可了),我們可以自定義訪問器的代碼,保證了外部修改的安全性。
所以我們才建議將字段作為類中的私有變量,屬性作為公共變量,通過屬性的設置,我們既可以靈活的定義讀寫字段時會發生什么事情,也可以很好的封裝字段以實現保護的目的。
不同的使用情況
公共字段:
- 允許自由讀寫
- 取值范圍只收數據類型約束而無其他任何特定限制;
- 值的變動不需要引發類中其它任何成員的相應變化;
如果滿足上述情況,則可以自由使用public 的字段,但是還是不建議使用
屬性:
- 要求字段只能讀或只能寫;
- 需要限定字段的取值范圍;
- 在改變一個字段的值的時候希望改變對象的其它一些狀態;
然而為什么我們建議使用屬性而非公共字段,從設計的耦合性和項目的可維護性來舉例,假如我們只是公開了這個字段,一旦我們想要修改這個字段的某些處理邏輯,那就必須對每一個引用了該字段的類中都進行處理。而使用屬性,我們則只需要設置get方法即可。
實際上屬性并不占用內存,只是用起來冗余一點,但是對于工程的提升是實打實的。
并且.net也提供了語法糖,我們只需定義屬性即可使用同名字段了(打出prop+回車 自動補全):
public int Index{ get; set; }