当前位置:  编程技术>.net/c#/asp.net

C#不可变类型深入解析

    来源: 互联网  发布时间:2014-11-01

    本文导语:  学过C#的人都知道string类型,但是string作为一种特殊的引用类型还有一个重要的特征就是恒定性,或者叫不可变性,即Immutable。作为不可变类型,最主要的特性表现是:一旦创建,只要修改,就会在托管堆上创建一个新的对象...

学过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),就会报错:


    
 
 
 
本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • Python函数默认参数和字典参数及可变参数(带星号参数)
  • java 方法能不能让像c中那样:行参是可变的(指的是个数和类型)
  • C++可变参数模板(variadic template)详细介绍及代码举例
  • java可变参数使用示例
  • shell中的可变参数问题
  • Java不可变List类型 JavaTuples
  • java虚拟机的内存大小是否可变
  • 怎么把一个可变长的字符串的最后一位付给另外一个变量?
  • nohup 日志能否 可变
  • 关于可变长数组VLA的一点疑问
  • c++11可变参数使用示例
  • 请问如何在jsp中include一个可变的变量文件名,谢谢!
  • 用C或C++编程,模拟可变分区存储管理且首次适应的算法实现存储器的分配与回收
  • 如何定义一个可变参数的自定义函数
  • 深入Java不可变类型的详解
  • 在可变式分区分配方案中,只需要进行一次比较就可以判定系统是能否满足作业对主存空间要求的是( )。
  • "Integer对象是不可变的"-----关于final和封装器的问题,望高手指点!
  • c语言可变参数实现示例
  • 用c语言根据可变参数合成字符串的实现代码
  • 不可变数据集合 Immutable.js
  • aix53中宏中使用可变参数的问题


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3