Effective C# Item6:明辨值类型和引用类型的使用场合
来源:互联网 发布:盘古排序指标源码 编辑:程序博客网 时间:2024/06/05 15:20
可能是受到Java的影响,我在转向C#后,一直没有注意区分过值类型和引用类型,不论是编写新代码还是重构旧代码时,如果发现有一些信息需要独立出来时,一直都是以新建类的方式进行,很少考虑使用结构体(我到目前为止,对结构体的理解还停留在C语言的层次)。
C#,或者说.NET,是区分值类型和引用类型的,这一点和C++以及Java都有区别。C++中传参都是以“传值”的方式进行,这种方式效率很高,但是会产生“对象切割”的问题,即在如果基类对象的地方,如果传递了一个派生类的实例,那么程序只会截取派生类实例中包含的基类信息,而忽略派生类自己新追加的信息,对于虚方法,也只会调用基类的方法;Java为了解决这个问题,将传参都做成了“按引用”的方式,这样造成了效率比较低。
我们在编写C#代码的过程中,应该在新建一个类型时,就要明确这个类型应该是值类型,还是引用类型。如果初期考虑不周全,到了后面再进行修改,很可能会造成问题。
区分值类型和引用类型的一个原则:值类型用于存储数据,而引用类型用于定义行为。
其实,对于单纯存储数据的结构,是采用值类型,还是存储类型,也可以从类的职责划分角度来看,无论是《敏捷软件开发》还是《设计模式》中,都对类的划分有说明。作为一个类,应该有其自身的职责,这些职责是通过向外提供一组接口的方式来实现的,对于单纯的存储数据的操作,例如ORM框架或者MVC模式中用于保存和DB表映射关系的Model,它本身没有行为,只是一堆数据,这种情况下,使用类来存储是否合适呢?
值类型是直接存储在堆栈上,而引用类型是存储在堆中,对于值类型来说,用户拿到的就是值类型本身,而对于引用类型来说,用户拿到的是指向引用类型的一个引用,这里,可以理解为指针。
我们可以看以下的代码。
首先,通过类的方式来定义一个结构
引用类型定义的结构 public class RefForStoreValue : ICloneable { private string m_strName; public string Name { get { return m_strName; } set { m_strName = value; } } private string m_strSex; public string Sex { get { return m_strSex; } set { m_strSex = value; } } private string m_strAddress; public string Address { get { return m_strAddress; } set { m_strAddress = value; } } public override string ToString() { return string.Format("Name:{0}, Sex:{1}, Address:{2}", this.Name, this.Sex, this.Address); } public object Clone() { return this.MemberwiseClone(); } }
然后,通过结构体的方式,来定义相同的结构
值类型定义的结构 public struct ValueForStoreValue { private string m_strName; public string Name { get { return m_strName; } set { m_strName = value; } } private string m_strSex; public string Sex { get { return m_strSex; } set { m_strSex = value; } } private string m_strAddress; public string Address { get { return m_strAddress; } set { m_strAddress = value; } } public override string ToString() { return string.Format("Name:{0}, Sex:{1}, Address:{2}", this.Name, this.Sex, this.Address); } }
接下来是很对上述代码的测试程序
测试程序 private static void TestRefType() { Console.WriteLine(); Console.WriteLine("Test Ref Type"); RefForStoreValue refValue = new RefForStoreValue(); refValue.Name = "AAA"; refValue.Sex = "F"; refValue.Address = "Beijing China"; Console.WriteLine(refValue.ToString()); Console.WriteLine(); RefForStoreValue refValue2 = refValue; RefForStoreValue refValue3 = refValue.Clone() as RefForStoreValue; refValue2.Name = "XXX"; refValue2.Sex = "NA"; refValue2.Address = "Moon"; Console.WriteLine(refValue.ToString()); Console.WriteLine(refValue2.ToString()); Console.WriteLine(refValue3.ToString()); } private static void TestValueType() { Console.WriteLine(); Console.WriteLine("Test Value Type"); ValueForStoreValue valueValue = new ValueForStoreValue(); valueValue.Name = "BBB"; valueValue.Sex = "M"; valueValue.Address = "Shanghai China"; Console.WriteLine(valueValue.ToString()); Console.WriteLine(); ValueForStoreValue valueValue2 = valueValue; valueValue2.Name = "XXX"; valueValue2.Sex = "NA"; valueValue2.Address = "Moon"; Console.WriteLine(valueValue.ToString()); Console.WriteLine(valueValue2.ToString()); }
最后,执行结果如下所示
这样,应该可以看出值类型和引用类型的区别了吧。
另外一点需要说明,在声明某一个类型的数组时,在分配存储空间方面,值类型是一次完成所有数组元素的分配工作,而引用类型,第一次只是分配了指向这个数组的引用,至于数组中的每一个元素,都是null。
在创建一个类型时,如果以下问题的回答都是“是”,那么我们就可以将其定义为值类型:
- 该类型的主要职责是否用于数据存储?
- 该类型的公有接口是否完全由一些数据成员存取属性所定义?
- 是否确信该类型永远不可能有子类?
- 是否确信该类型永远都不可能具有多态行为?
如果对于上述问题的答案不太确定,那我们还是将其定义为引用类型吧。
- Effective C# Item6:明辨值类型和引用类型的使用场合
- 明辨值类型和引用类型的使用场合
- 明辨值类型和引用类型的使用场合
- 明辨值类型和引用类型的使用场合
- 明辨值类型和引用类型的使用场合
- 改善C#程序的50种方法 条款6:明辨值类型和引用类型的使用场合
- 条款6:明辨值类型和引用类型的使用场合
- C#的值类型和引用类型
- C#的引用类型和值类型
- C#的值类型和引用类型
- C++和C#有关值类型和引用类型,以及对引用类型使用ref的效果
- Effective C# 原则6:区别值类型数据和引用类型数据
- 《Effective C#》Item 6:区分值类型和引用类型
- 《Effective C#》Item 6:区分值类型和引用类型
- C#中的值类型和引用类型
- c#基础 值类型和引用类型
- c# 引用类型和值类型
- 关于c#值类型和引用类型
- CGContextSaveGState与CGContextRestoreGState的功用
- php 框架thinkphp里自写的常用函数
- 罗网营销教您企业营销方法
- mysql的简单实例
- 获取到插入条目后的自递增的值
- Effective C# Item6:明辨值类型和引用类型的使用场合
- 我的博客开通了!!!!!
- Android 4.4开发中向SD存储卡写入文件方案
- hibernate技巧集合
- 赵雅智_名片夹(5)_Android中listview可折叠伸缩仿手风琴效果(动态)
- 数据库(多表查询)
- Linux中IRC通讯工具Pidgin的基本使用方法
- ubuntu 安装android sdk
- KMP 算法详解