C#語言入門詳解(17)字段、屬性、索引器、常量
- 前言
- 一、字段 Field
- 二、屬性
- 三、索引器
- 四、常量
內容來自劉鐵猛C#語言入門詳解課程。
參考文檔:CSharp language specification 5.0 中文版
前言
類的成員是靜態成員 (static member) 或者實例成員 (instance member)。 靜態成員屬于類, 實例成員屬于對象(類的實例) 。下表提供了類所能包含的成員種類的概述。
成員 | 說明 |
---|---|
常量 | 與類關聯的常量值 |
字段 | 類的變量 |
方法 | 類可執行的計算和操作 |
屬性 | 與讀寫類的命名屬性相關聯的操作 |
索引器 | 與以數組方式索引類的實例相關聯的操作 |
事件 | 可由類生成的通知 |
運算符 | 類所支持的轉換和表達式運算符 |
構造函數 | 初始化類的實例或類本身所需的操作 |
析構函數 | 在永久丟棄類的實例之前執行的操作 |
類型 | 類所聲明的嵌套類型 |
本節所講的字段、屬性、索引器、常量為類的四個成員。
一、字段 Field
- field原意為田地,在此處意指為數據存放空間。以Student類為例,包含ID、Name兩個字段,每個字段為4個字節。則Student的對象占8個字節,這8個字節分為兩塊田地,分別為ID、Name放不同的數據。
- 使用 static 修飾符聲明的字段定義靜態字段 (static field)。 一個靜態字段只標識一個存儲位置。無論對一個類創建多少個實例, 它的靜態字段永遠都只有一個副本,可用來表示類當前的狀態,即靜態字段的初始化在運行環境加載類時,后面不再初始化。聲明靜態字段時設置初始化值與在靜態構造器里面初始化靜態字段其實是一樣的。
- 不使用 static 修飾符聲明的字段定義實例字段 (instance field)。 類的每個實例都為該類的所有實例字段創建一個單獨副本,每次創建實例都會初始化字段,實例字段的初始化在創建時。聲明實例字段時初始化值與在實例構造器里面初識化實例字段是一樣的。
- readonly修飾的字段只有一次賦值機會,可在聲明時或構造器中賦值。
class Program
{static void Main(string[] args){var stuList = new List<Student>();for (int i = 0; i < 100; i++){var stu = new Student{Age = 24,Score = i,};stuList.Add(stu);}int totalAge=0;int totalScore=0;foreach (var stu in stuList){totalAge += stu.Age;totalScore += stu.Score;}//調用靜態字段,可直接使用 類.字段 的形式Student.AverageAge = totalAge / Student.Amout;Student.AverageScore = totalScore / Student.Amout;Student.ReportAmount();Student.ReportAverageAge();Student.ReportScore();}class Student{//普通字段public readonly int ID;public int Age;public int Score;//靜態字段//public、static為修飾符,修飾符在使用中最少使用一個,且必須是有意義的//訪問級別 static修飾符 變臉類型 成員名public static int AverageAge;public static int AverageScore;public static int Amout;// readonly修飾的字段只有一次賦值機會public Student(int id){this.ID = id;}public Student(){Amout++;}//靜態方法public static void ReportAmount(){Console.WriteLine(Amout);}public static void ReportAverageAge(){Console.WriteLine(AverageAge);}public static void ReportScore(){Console.WriteLine(AverageScore);}}
}
二、屬性
- 屬性為字段的包裝器,為避免字段被污染,需要對字段的賦值制定一定的規則,這個規則通過屬性來實現。
- prop + 2 * TAB:屬性的簡略聲明
- propfull + 2 * TAB:屬性的完整聲明
- 使用 Get/Set 方法對之前:
class Program
{static void Main(string[] args){var stu1 = new Student(){Age = 20};var stu2 = new Student(){Age = 20};var stu3 = new Student(){// 非法值,污染字段Age = 200};var avgAge = (stu1.Age + stu2.Age + stu3.Age) / 3;Console.WriteLine(avgAge);}
}class Student
{public int Age;
}
- 在C++/Java等編程語言中演化為 Get/Set 方法:
class Program
{static void Main(string[] args){try{var stu1 = new Student();stu1.SetAge(20);var stu2 = new Student();stu2.SetAge(20);var stu3 = new Student();stu3.SetAge(200);var avgAge = (stu1.GetAge() + stu2.GetAge() + stu3.GetAge()) / 3;Console.WriteLine(avgAge);}catch (Exception ex){Console.WriteLine(ex.Message);}}
}class Student
{private int age;public int GetAge(){return age;}public void SetAge(int value){if (value >= 0 && value <= 120){age = value;}else{throw new Exception("Age value has error.");}}
}
- C++、JAVA 里面是沒有屬性的,使用 Get/Set 來保護字段的方法至今仍在 C++、JAVA 里面流行。因為 Get/Set 寫起來冗長,微軟應廣大程序員請求,給 C# 引入了屬性。
class Program
{static void Main(string[] args){try{var stu1 = new Student();stu1.Age=20;var stu2 = new Student();stu2.Age = 20;var stu3 = new Student();stu3.Age = 200;var avgAge = (stu1.Age + stu2.Age + stu3.Age) / 3;Console.WriteLine(avgAge);}catch (Exception ex){Console.WriteLine(ex.Message);}}
}class Student
{private int age;public int Age{get{return age;}set{if (value >= 0 && value <= 120){age = value;}else{throw new Exception("Age value has error.");}}}
}
- 動態計算值的屬性。主動計算,每次獲取 CanWork 時都計算,適用于 CanWork 屬性使用頻率低的情況。
class Program
{static void Main(string[] args){try{var stu1 = new Student();stu1.Age = 12;Console.WriteLine(stu1.CanWork);}catch (Exception ex){Console.WriteLine(ex.Message);}}
}class Student
{private int age;public int Age{get { return age; }set{age = value;}}public bool CanWork{get{return age > 16;}}
}
- 被動計算,只在 Age 賦值時計算一次,適用于 Age 屬性使用頻率低,CanWork 使用頻率高的情況。
class Program
{static void Main(string[] args){try{var stu1 = new Student();stu1.Age = 12;Console.WriteLine(stu1.CanWork);}catch (Exception ex){Console.WriteLine(ex.Message);}}
}class Student
{private int age;public int Age{get { return age; }set{age = value;CalculateCanWork();}}private bool canWork;public bool CanWork{get { return canWork; }}private void CalculateCanWork(){canWork = age > 16;}
}
- 建議:永遠使用屬性(而不是字段)來暴露數據,即字段永遠是 private 或 protected 的。 字段只在類內部使用,類之間交換數據,永遠只用屬性。
三、索引器
- indexer + 2 * TAB:快速聲明索引器
- 索引器一般用于集合中,下面示例僅為展示,一般很少用在非集合中。
class Program
{static void Main(string[] args){var stu = new Student();stu["Math"] = 90;stu["Math"] = 100;var mathScore = stu["Math"];Console.WriteLine(mathScore);}
}class Student
{//鍵值對,string為科目名稱,int為socreprivate Dictionary<string, int> scoreDictionary = new Dictionary<string, int>();public int? this[string subject]{get{if (scoreDictionary.ContainsKey(subject)){return scoreDictionary[subject];}else{return null;}}set{if (value.HasValue == false){throw new Exception("Score cannot be null");}if (scoreDictionary.ContainsKey(subject)){// 可空類型的 Value 屬性才是其真實值。scoreDictionary[subject] = value.Value;}else{scoreDictionary.Add(subject, value.Value);}}}
}
四、常量
- 示例為第四種場景:類不可以用作常量,需使用靜態只讀字段
class Program
{static void Main[string[] args){Console.WriteLine[WASPEC.WebsiteURL);}
}class WASPEC
{public const string WebsiteURL = "http://www.waspec.org";//類 不能用作常量//public const Building Location = new Building[) { Address = "Some Address” };public static readonly Building Location = new Building("Some Address")
class Building}class Building
{public Building[string address){this.Address = address;}public string Address { get; set; }
}