常量、欄位、方法、屬性和索引器(C#)
常量(Constants)
常量是在編譯時已知並在程式的生存期內不發生更改的不可變值。 常量使用 const 修飾符進行宣告。 只有 C# 內建型別(System.Object 除外)可以宣告為 const。使用者定義的型別(包括類、結構和陣列)不能為 const。 請使用 readonly 修飾符建立在執行時初始化一次即不可再更改的類、結構或陣列。
注:內建型別表如下
常量必須在宣告時初始化。例如:
C#:
class Calendar1
{
public const int months = 12;
}
在此示例中,常量 months 始終為 12,不可更改,即使是該類自身也不能更改它。 實際上,當編譯器遇到 C# 原始碼(例如 months)中的常量修飾符時,將直接把文字值替換到它生成的中間語言 (IL) 程式碼中。 因為在執行時沒有與常量關聯的變數地址,所以 const 欄位不能通過引用傳遞,並且不能在表示式中作為左值出現。
欄位(fields )
“欄位”是直接在類或結構中宣告的任何型別的變數。 欄位是其包含型別的“成員”。
類或結構可以擁有例項欄位(instance fields)或靜態欄位( static fields ),或同時擁有兩者。 例項欄位特定於型別的例項。 如果您擁有類 T 和例項欄位 F,可以建立型別 T 的兩個物件,並修改每個物件中 F 的值,這不影響另一物件中的該值。 相比之下,靜態欄位屬於類本身,在該類的所有例項中共享。 從例項 A 所做的更改將立刻呈現在例項 B 和 C 上(如果它們訪問該欄位)。
通常應僅為具有私有或受保護可訪問性的變數使用欄位。 您的類向客戶端程式碼公開的資料應通過方法、屬性和索引器提供。 通過使用這些構造間接訪問內部欄位,可以針對無效的輸入值提供防護。 儲存由公共屬性公開的資料的私有欄位稱為“後備儲存”或“支援欄位”。
- 在類塊中通過指定欄位的訪問級別,然後指定欄位的型別,再指定欄位的名稱來宣告這些欄位。 例如:
C#:
public class CalendarEntry
{
// private field
private DateTime date;
// public field (Generally not recommended.)
public string day;
...
}
- 若要訪問物件中的欄位,請在物件名稱後面新增一個句點,然後新增該欄位的名稱,比如 objectname.fieldname。 例如:
C#:
CalendarEntry birthday = new CalendarEntry();
birthday.day = "Saturday";
- 宣告欄位時可以使用賦值運算子為欄位指定一個初始值。 例如,若要自動將 “Monday” 賦給 day 欄位,需要宣告 day,如下例所示:
C#:
public class CalendarDateWithInitialization
{
public string day = "Monday";
//...
}
- 欄位可標記為 public、private、protected、internal 或 protected internal。 這些訪問修飾符定義類的使用者訪問欄位的方式。
注:訪問修飾符
可以選擇將欄位宣告為 static。 這使得呼叫方在任何時候都能使用欄位,即使類沒有任何例項。
可以將欄位宣告為 readonly。 只讀欄位只能在初始化期間或在建構函式中賦值。 static readonly 欄位非常類似於常數,只不過 C# 編譯器不能在編譯時訪問靜態只讀欄位的值,而只能在執行時訪問。
注:變數與欄位的區別與聯絡
public class Test
{
// field在這裡是一個欄位變數
public int field =0;
// 靜態欄位,
public static int filed2 =1;
public void TestMethod()
{
// 此時a是一個變數,並且是一個區域性變數,a就不是欄位了
// 而對於field 和field2我們最好認為其是一個欄位,但是它也是變數,我們一般叫法為 “欄位變數”
int a =2;
Console.Write(a);
}
}
方法(method)
方法是包含一系列語句的程式碼塊。 程式通過呼叫該方法並指定任何所需的方法引數使語句得以執行。 在 C# 中,每個執行的指令均在方法的上下文中執行。 Main 方法是每個 C# 應用程式的入口點,並在啟動程式時由公共語言執行時 (CLR) 呼叫。
方法簽名
- 通過指定訪問級別(如 public 或 private)、可選修飾符(如 abstract 或 sealed)、返回值、方法的名稱以及任何方法引數,在類或結構中宣告方法。 這些部件一起構成方法的簽名。
- 方法引數在括號內,並且用逗號分隔。 空括號指示方法不需要任何引數。
注:出於方法過載的目的,方法的返回型別不是方法簽名的一部分。 但是在確定委託和它所指向的方法之間的相容性時,它是方法簽名的一部分。
補充:方法過載是指在一個類中定義多個同名的方法,但要求每個方法具有不同的引數的型別或引數的個數。比如:
public void Func(){}
public void Func(int a){}
public void Func(int a,int b){}
···
Func(a);//會呼叫public void Func(int a)這個版本
具體規範:
- 方法名一定要相同。
- 方法的引數表必須不同,包括引數的型別或個數,以此區分不同的方法體。(如果引數個數不同,就不管它的引數型別了。如果引數個數相同,那麼引數的型別或者引數的順序必須不同。)
- 方法的返回型別、修飾符可以相同,也可不同。即在C#中不能只根據返回值的型別不同來區分不同的過載函式。
方法訪問
呼叫物件上的方法就像訪問欄位。 在物件名之後新增一個句點、方法名和括號。 引數列在括號裡,並且用逗號分隔。
方法引數與引數
該方法定義指定任何所需引數的名稱和型別。呼叫程式碼呼叫該方法時,它為每個引數提供了稱為引數的具體值。 引數必須與引數型別相容,但呼叫程式碼中使用的引數名(如果有)不需要與方法中定義的引數名相同。
按引用傳遞與按值傳遞
預設情況下,值型別傳遞給方法時,傳遞的是副本而不是物件本身。 因此,對引數的更改不會影響呼叫方法中的原始副本。可以使用 ref 關鍵字按引用傳遞值型別。
引用型別的物件傳遞到方法中時,將傳遞對物件的引用。 也就是說,該方法接收的不是物件本身,而是指示該物件位置的引數。 如果通過使用此引用更改物件的成員,即使是按值傳遞該物件,此更改也會反映在呼叫方法的引數中。
通過使用 class 關鍵字建立引用型別,如以下示例所示。
public class SampleRefType
{
public int value;
}
現在,如果將基於此型別的物件傳遞到方法,則將傳遞對物件的引用。 下面的示例將 SampleRefType 型別的物件傳遞到 ModifyObject 方法。
public static void TestRefType()
{
SampleRefType rt = new SampleRefType();
rt.value = 44;
ModifyObject(rt);
Console.WriteLine(rt.value);
}
static void ModifyObject(SampleRefType obj)
{
obj.value = 33;
}
該示例執行的內容實質上與先前示例相同,均按值將引數傳遞到方法。 但是因為使用了引用型別,結果有所不同。 ModifyObject 中所做的對形參 obj 的 value 欄位的修改,也會更改 TestRefType 方法中實參 rt 的 value 欄位。 TestRefType 方法顯示 33 作為輸出。
返回值
方法可以將值返回到呼叫方。如果列在方法名之前的返回型別不是 void,則該方法可通過使用 return 關鍵字返回值。帶 return 關鍵字,後跟與返回型別匹配的值的語句將該值返回到方法呼叫方。return 關鍵字還會停止執行該方法。如果返回型別為 void,沒有值的 return 語句仍可用於停止執行該方法。 沒有 return 關鍵字,當方法到達程式碼塊結尾時,將停止執行。具有非空的返回型別的方法都需要使用 return 關鍵字來返回值。例如,這兩種方法都使用 return 關鍵字來返回整數:
class SimpleMath
{
public int AddTwoNumbers(int number1, int number2)
{
return number1 + number2;
}
public int SquareANumber(int number)
{
return number * number;
}
}
若要使用從方法返回的值,呼叫方法可以在相同型別的值足夠的地方使用該方法呼叫本身。 也可以將返回值分配給變數。 例如,以下兩個程式碼示例實現了相同的目標:
int result = obj.AddTwoNumbers(1, 2);
result = obj.SquareANumber(result);
// The result is 9.
Console.WriteLine(result);
result = obj.SquareANumber(obj.AddTwoNumbers(1, 2));
// The result is 9.
Console.WriteLine(result);
屬性(property)
屬性是一種成員,它提供靈活的機制來讀取、寫入或計算私有欄位的值。 屬性可用作公共資料成員,但它們實際上是稱為“訪問器”的特殊方法。 這使得可以輕鬆訪問資料,還有助於提高方法的安全性和靈活性。
屬性結合了欄位和方法的多個方面。 對於物件的使用者,屬性顯示為欄位,訪問該屬性需要相同的語法。 對於類的實現者,屬性是一個或兩個程式碼塊,表示一個 get 訪問器和/或一個 set 訪問器。 當讀取屬性時,執行 get 訪問器的程式碼塊;當向屬性分配一個新值時,執行 set 訪問器的程式碼塊。 不具有 set 訪問器的屬性被視為只讀屬性。 不具有 get 訪問器的屬性被視為只寫屬性。 同時具有這兩個訪問器的屬性是讀寫屬性。
與欄位不同,屬性不作為變數來分類。 因此,不能將屬性作為 ref(C# 參考)引數或 out(C# 參考)引數傳遞。
屬性具有多種用法:它們可在允許更改前驗證資料;它們可透明地公開某個類上的資料,該類的資料實際上是從其他源(例如資料庫)檢索到的;當資料被更改時,它們可採取行動,例如引發事件或更改其他欄位的值。
屬性在類塊中是按以下方式來宣告的:指定欄位的訪問級別,接下來指定屬性的型別和名稱,然後跟上宣告 get 訪問器和/或 set 訪問器的程式碼塊。 例如:
public class Date
{
private int month = 7; // Backing store
public int Month
{
get
{
return month;
}
set
{
if ((value > 0) && (value < 13))
{
month = value;
}
}
}
}
在此示例中,Month 是作為屬性宣告的,這樣 set 訪問器可確保 Month 值設定為 1 和 12 之間。 Month 屬性使用私有欄位來跟蹤該實際值。 屬性的資料的真實位置經常稱為屬性的“後備儲存”。屬性使用作為後備儲存的私有欄位是很常見的。 將欄位標記為私有可確保該欄位只能通過呼叫屬性來更改。
get 訪問器
get 訪問器體與方法體相似。 它必須返回屬性型別的值。 執行 get 訪問器相當於讀取欄位的值。
以下是返回私有欄位 name 的值的 get 訪問器:
class Person
{
private string name; // the name field
public string Name // the Name property
{
get
{
return name;
}
}
}
當引用屬性時,除非該屬性為賦值目標,否則將呼叫 get 訪問器以讀取該屬性的值。 例如:
Person person = new Person();
//...
System.Console.Write(person.Name); // the get accessor is invoked here
get 訪問器必須以 return 或 throw 語句終止,並且控制權不能離開訪問器體。
通過使用 get 訪問器更改物件的狀態不是一種好的程式設計風格。 例如,以下訪問器在每次訪問 number 欄位時都會產生更改物件狀態的副作用。
private int number;
public int Number
{
get
{
return number++; // Don't do this
}
}
get 訪問器可用於返回欄位值,或用於計算並返回欄位值。 例如:
class Employee
{
private string name;
public string Name
{
get
{
return name != null ? name : "NA";
}
}
}
在上一個程式碼段中,如果不對 Name 屬性賦值,它將返回值 NA。
set 訪問器
set 訪問器類似於返回型別為 void 的方法。 它使用稱為 value 的隱式引數,此引數的型別是屬性的型別。 在下面的示例中,將 set 訪問器新增到 Name 屬性:
class Person
{
private string name; // the name field
public string Name // the Name property
{
get
{
return name;
}
set
{
name = value;
}
}
}
當對屬性賦值時,用提供新值的引數呼叫 set 訪問器。 例如:
Person person = new Person();
person.Name = "Joe"; // the set accessor is invoked here
System.Console.Write(person.Name); // the get accessor is invoked here
在 set 訪問器中,對區域性變數宣告使用隱式引數名稱 value 是錯誤的。
備註
- 可將屬性標記為 public、private、protected、internal 或 protected internal。 這些訪問修飾符定義類的使用者如何才能訪問屬性。 同一屬性的 get 和 set 訪問器可能具有不同的訪問修飾符。 例如,get 可能是 public 以允許來自型別外的只讀訪問;set 可能是 private 或 protected。 有關更多資訊,請參見 訪問修飾符(C# 程式設計指南)。
- 可以使用 static 關鍵字將屬性宣告為靜態屬性。 這使得呼叫方隨時可使用該屬性,即使不存在類的例項。 有關更多資訊,請參見 靜態類和靜態類成員(C# 程式設計指南)。
- 可以使用 virtual 關鍵字將屬性標記為虛屬性。 這樣,派生類就可以通過使用 override 關鍵字來重寫事件行為。 有關這些選項的更多資訊,請參見 繼承(C# 程式設計指南)。
- 重寫虛屬性的屬性還可以是 sealed 的,這表示它對派生類不再是虛擬的。 最後,可以將屬性宣告為 abstract。 這意味著類中沒有任何實現,派生類必須編寫自己的實現。 有關這些選項的更多資訊,請參見 抽象類、密封類及類成員(C# 程式設計指南)。
- 對 static 屬性的訪問器使用 virtual、abstract或 override修飾符是錯誤的。
索引器(Indexers)
- 索引器允許類或結構的例項就像陣列一樣進行索引。 索引器類似於屬性,不同之處在於它們的取值函式採用引數。
- 使用索引器可以用類似於陣列的方式為物件建立索引。
- get 取值函式返回值。 set 取值函式分配值。
- this 關鍵字用於定義索引器。
- value 關鍵字用於定義由 set 索引器分配的值。
- 索引器不必根據整數值進行索引;由你決定如何定義 特定的查詢機制。
- 索引器可被過載。
- 索引器可以有多個形參,例如當訪問二維陣列時。
在下面的示例中,定義了一個泛型類,併為其提供了簡單的 get 和 set 取值函式方法(作為分配和檢索值的方法)。 Program 類建立了此類的一個例項,用於儲存字串。
class SampleCollection<T>
{
// Declare an array to store the data elements.
private T[] arr = new T[100];
// Define the indexer, which will allow client code
// to use [] notation on the class instance itself.
// (See line 2 of code in Main below.)
public T this[int i]
{
get
{
// This indexer is very simple, and just returns or sets
// the corresponding element from the internal array.
return arr[i];
}
set
{
arr[i] = value;
}
}
}
// This class shows how client code uses the indexer.
class Program
{
static void Main(string[] args)
{
// Declare an instance of the SampleCollection type.
SampleCollection<string> stringCollection = new SampleCollection<string>();
// Use [] notation on the type.
stringCollection[0] = "Hello, World";
System.Console.WriteLine(stringCollection[0]);
}
}
// Output:
// Hello, World.