• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C#教程 > C#不可变类型深入解析

C#不可变类型深入解析

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c#数据类型,c#引用类型,c#枚举类型的用法,c#数据类型转换,c#值类型和引用类型等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

学过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);
    }
  }
 
</div>

在主程序中故意制造出一个异常,目的是只对一个变量所代表类型的某些成员赋值。

    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();
    }
 
</div>

打断点,运行,发现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);
    }
  }
 
</div>

运行结果如下图所示:

由此可见,我们无法修改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));
    }
  }
 
</div>

结果如下图所示:

由此可见,还是可以对对象的属性间接修改赋值,不满足不可变类型的"常量性"特点。

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));
    }
  }
 
</div>

运行结果如下图所示:

此外,如果让分数不满足条件,Student student = new Student("张三", "8", classes),就会报错:

</div>
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C#中this用法系列(二) 通过this修饰符为原始类型扩展方法
  • C#中关于可空类型的小知识
  • C#预定义数据类型之值类型和引用类型介绍
  • C#引用类型转换的常见方式总结
  • C#不可变类型深入解析
  • C#判断数据类型的简单示例代码
  • c#可空类型的作用说明
  • C#常用的数据格式转换汇总
  • 浅析C#数据类型转换的几种形式
  • c#根据文件类型获取相关类型图标的方法代码

相关文章

  • 2017-05-28基于mvc5+ef6+Bootstrap框架实现身份验证和权限管理
  • 2017-05-28将文件夹下所有文件输出到日志文件中 c#递归算法学习示例
  • 2017-05-28c#线程Thread示例
  • 2017-05-28微信服务号推送模板消息接口
  • 2017-05-28c#转义字符串中的所有正则特殊字符方法示例
  • 2017-05-28C# 从枚举值获取对应的文本描述详解
  • 2017-05-28C#数值转换-隐式数值转换表参考
  • 2017-05-28c#使用DotNetZip封装类操作zip文件(创建/读取/更新)实例
  • 2017-05-28C#使用linq查询大数据集的方法
  • 2017-05-28C#对Access进行增删改查的完整示例

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C#实现在Form里面内嵌dos窗体的方法
    • C#的内存回收代码
    • 区分WCF与WebService的异同、优势
    • c#构造ColorComboBox(颜色下拉框)
    • C#实现下拉框绑定list集合的方法
    • C#抽象类和接口的区别分析
    • C#中的ICustomFormatter及IFormatProvider接口用法揭秘
    • C#中的DataSet、string、DataTable、对象转换成Json的实现代码
    • C# SendKeys使用方法介绍
    • 使用c#开发公众平台自定义菜单功能

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有