国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C# > 正文

C#不可變類型深入解析

2020-01-24 02:32:08
字體:
來源:轉載
供稿:網友

學過C#的人都知道string類型,但是string作為一種特殊的引用類型還有一個重要的特征就是恒定性,或者叫不可變性,即Immutable。作為不可變類型,最主要的特性表現是:一旦創建,只要修改,就會在托管堆上創建一個新的對象實例,而且和上一個對象實例是相鄰的,在托管堆上分配到一塊連續的內存空間。

那么為什么需要不可變類型呢?

在多線程情況下,一個線程,由于種種原因(比如異常)只修改了一個變量所代表類型的部分成員的值,這時候,另一個進程進來,也訪問這個變量,第二個進程訪問到的變量成員,一部分成員還是原來的值,另一部分成員的值是第一個線程修改的值,這樣就出現了"數據不一致"。而不可變類型就是為了解決在多線程條件下的"數據不一致"的問題。

當然,字符串的不可變性或恒定性,不僅解決了"數據不一致"的問題,還為字符串的"駐留"提供了前提,這樣才可以把不同的字符串以及托管堆上的內存地址以鍵值對的形式放到全局哈希表中。

一、親眼目睹"數據不一致":

對Student的Score屬性,在賦值的時候加上檢測,檢測是否是2位數整數。

  public struct Student  {    private string name;    private string score;     public string Name    {      get { return name; }      set { name = value; }    }     public string Score    {      get { return score; }      set      {        CheckScore(value);        score = value;      }    }     //檢測分數是否是2位數整數    private void CheckScore(string value)    {      string pattern = @"/d{2}";      if (!Regex.IsMatch(value, pattern))      {        throw new Exception("不是有效分數!");      }    }     public override string ToString()    {      return String.Format("姓名:{0},分數:{1}", name, score);    }  } 

在主程序中故意制造出一個異常,目的是只對一個變量所代表類型的某些成員賦值。

    static void Main(string[] args)    {      Student student = new Student();      student.Name = "張三";      student.Score = "80";      Console.WriteLine(student.ToString());       try      {        student.Name = "李四";        student.Score = "8";      }      catch (Exception)      {                throw;      }      Console.WriteLine(student.ToString());      Console.ReadKey();    } 

打斷點,運行,發現Student類型的student變量,在第二次賦值的時候,把student的Name屬性值改了過來,而student的Score屬性,由于發生了異常,沒有修改過來。這就是"數據不一致"。

如下圖所示:

二、動手設計不可變類型

1.不可變類型的2個特性:

①對象的原子性:要么不改,要改就把所有成員都改,從而創建新的對象。
②對象的常量性:對象一旦創建,就不能改變狀態,即不能改變對象的屬性,只能創建新的對象。

2.遵循以上不可變類型的2個特征

①在構造函數中對所有字段賦值。
②將屬性中的set訪問器刪除。

  class Program  {    static void Main(string[] args)    {      Student student = new Student("張三", "90");      student = new Student("李四","80");      Console.WriteLine(student.ToString());      Console.ReadKey();    }  }   public struct Student  {    private readonly string name;    private readonly string score;     public Student(string name, string score)    {      this.name = name;      this.score = score;    }     public string Name    {      get { return name; }    }     public string Score    {      get { return score; }    }     public override string ToString()    {      return String.Format("姓名:{0},分數:{1}", name, score);    }  } 

運行結果如下圖所示:

由此可見,我們無法修改Student的其中某一個成員,只能通過構造函數創建一個新對象,滿足"對象的原子性"。
而且也無法修改Student對象實例的某個屬性值,符合"對象的常量性"。

3.如果有引用類型字段和屬性,如何做到"不可變性"?

  class Program  {    static void Main(string[] args)    {      string[] classes = {"語文", "數學"};      Student student = new Student("張三", "85", classes);      Console.WriteLine("==修改之前==");      Console.WriteLine(student.ToString());       string[] tempArray = student.Classes;      tempArray[0] = "英語";      Console.WriteLine("==修改之后==");      Console.WriteLine(student.ToString());      Console.ReadKey();    }  }   public struct Student  {    private readonly string name;    private readonly string score;    private readonly string[] classes;     public Student(string name, string score, string[] classes)    {      this.name = name;      this.score = score;      this.classes = classes;    }     public string Name    {      get { return name; }    }     public string Score    {      get { return score; }    }     public string[] Classes    {      get { return classes; }    }     public override string ToString()    {      string temp = string.Empty;      foreach (string item in classes)      {        temp += item + ",";      }       return String.Format("姓名:{0},總分:{1},參加的課程有:{2}", name, score,temp.Substring(0, temp.Length -1));    }  } 

結果如下圖所示:

由此可見,還是可以對對象的屬性間接修改賦值,不滿足不可變類型的"常量性"特點。

4.通過在構造函數和屬性的get訪問器中復制的方式來滿足不可變性

  class Program  {    static void Main(string[] args)    {      string[] classes = {"語文", "數學"};      Student student = new Student("張三", "85", classes);      Console.WriteLine("==修改之前==");      Console.WriteLine(student.ToString());       string[] tempArray = student.Classes;      tempArray[0] = "英語";      Console.WriteLine("==修改之后==");      Console.WriteLine(student.ToString());      Console.ReadKey();    }  }   public struct Student  {    private readonly string name;    private readonly string score;    private readonly string[] classes;     public Student(string name, string score, string[] classes)    {      this.name = name;      this.score = score;      this.classes = new string[classes.Length];      classes.CopyTo(this.classes, 0);      CheckScore(score);    }     public string Name    {      get { return name; }    }     public string Score    {      get { return score; }    }     public string[] Classes    {      get      {        string[] result = new string[classes.Length];        classes.CopyTo(result,0);        return result;      }    }     //檢測分數是否是2位數整數    private void CheckScore(string value)    {      string pattern = @"/d{2}";      if (!Regex.IsMatch(value, pattern))      {        throw new Exception("不是有效分數!");      }    }     public override string ToString()    {      string temp = string.Empty;      foreach (string item in classes)      {        temp += item + ",";      }       return String.Format("姓名:{0},總分:{1},參加的課程有:{2}", name, score,temp.Substring(0, temp.Length -1));    }  } 

運行結果如下圖所示:

此外,如果讓分數不滿足條件,Student student = new Student("張三", "8", classes),就會報錯:

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 诸城市| 双辽市| 石城县| 铁岭县| 金阳县| 子长县| 泾川县| 通许县| 富阳市| 图片| 上蔡县| 库车县| 西乌珠穆沁旗| 平塘县| 大理市| 长垣县| 蕲春县| 麻栗坡县| 修文县| 长垣县| 安多县| 诸城市| 宣汉县| 同江市| 会东县| 泽库县| 安溪县| 冕宁县| 新津县| 怀柔区| 乌兰浩特市| 和静县| 合川市| 南投市| 区。| 荆门市| 宝丰县| 济阳县| 镇康县| 张家川| 张家川|