C#——值类型和引用类型

来源:互联网 发布:php exec 超时 编辑:程序博客网 时间:2024/06/06 04:47

在C#中,
值类型:整型、布尔型、字符型、实数型、结构型、枚举型。
引用类型:类、对象、字符串、数组、接口、委托。

类型描述   有符号整型:sbyte,short,int,long  无符号整型:byte,ushort,uint,ulong 简单类型Unicode字符:char  IEEE浮点型:float,double值类型 高精度小数:decimal  布尔型:bool 枚举类型用户自定义类型:enum 

结构类型

用户自定义类型:struct

  

 

所有其他类型的最终基类:object

 类类型Unicode字符串:string引用类型 用户自定义类型:class 接口类型用户自定义类型:Interface 数组类型单维与多维数组:例如,int[]与int[,] 委托类型用户自定义类型:delegate


区别:
1、值类型通常被分配在栈上,它的变量直接包含变量的实例,使用效率比较高。

2、引用类型分配在托管堆上,引用类型的变量通常包含一个指向实例的指针,变量通过该指针来引用实例。

3、值类型继承自ValueType(注意:而System.ValueType又继承自System.Object);而引用类型继承自System.Object。

4、值类型变量包含其实例数据,每个变量保存了其本身的数据拷贝(副本),因此在默认情况下,值类型的参数传递不会影响参数本身;而引用类型变量保存了其数据的引用地址,因此以引用方式进行参数传递时会影响到参数本身,因为两个变量会引用了内存中的同一块地址。

5、值类型有两种表示:装箱与拆箱;引用类型只有装箱一种形式。我会在下节以专门的篇幅来深入讨论这个话题。

6、典型的值类型为:struct,enum以及大量的内置值类型;而能称为类的都可以说是引用类型。

7、值类型的内存不由GC(垃圾回收,Gabage Collection)控制,作用域结束时,值类型会自行释放,减少了托管堆的压力,因此具有性能上的优势。例如,通常struct比class更高效;而引用类型的内存回收,由GC来完成,微软甚至建议用户最好不要自行释放内存。

8、值类型是密封的(sealed),因此值类型不能作为其他任何类型的基类,但是可以单继承或者多继承接口;而引用类型一般都有继承性。

9、值类型不具有多态性;而引用类型有多态性。

10、值类型变量不可为null值,值类型都会自行初始化为0值;而引用类型变量默认情况下,创建为null值,表示没有指向任何托管堆的引用地址。对值为null的引用类型的任何操作,都会抛出NullReferenceException异常。

11、值类型有两种状态:装箱和未装箱,运行库提供了所有值类型的已装箱形式;而引用类型通常只有一种形式:装箱。

 

 

补充描述:

 

无论是对于值类型还是引用类型来说,对于其作为函数参数或者返回值的时候,都是容易犯错误的地方。

 

对于值类型来说,当其作为函数参数的时候,希望在函数中被修改,那么直接如下操作是不能被修改的。

        public void Increment( int i )

        {

            i++;

        }

要想在函数中对传进去的参数做真正的修改,需要借助于ref这个关键字,那么正确的形式如下。

        public void Increment( ref int i )

        {

            i++;

        }

 

也就是说,如果需要在函数中对值类型参数进行修改,需要用ref或者out进行标识才能真正实现。

 

而对于引用类型来说,当其作为函数参数的时候,它所遇到的情况恰恰与值类型相反,即不希望在函数中被修改,举例如下。

        public void AddValue( MyType typValue )

        {

            typValue.Count = typValue.Count + 15;

        }

由于对于引用类型对象来说,其的赋值操作只是对原有对象的引用,因此在函数对其修改,实际上是直接修改了原有对象数据,这是很多情况不希望发生的(这里例如对数组或者DataTable操作这类)。

 

为了防止这种事发生,需要给此类型提供clone函数。例如对于如上的类型,可以入下实现。

    public class MyType:ICloneable

    {

        private int nCount = 0;

        public int Count

        {

            set{ nCount = value;}

            get{ return nCount;}

        }

        public MyType()

        {

        }

        public MyType( int Value)

        {

            nCount = Value;

        }

 

        #region ICloneable Members

 

        public object Clone()

        {

            // TODO:  Add MyType.Clone implementation

            return new MyType( nCount );

        }

 

        #endregion

    }

那么在调用的时候,用当前的对象的clone作为参数即可。

 

不过对于引用类型来说,提供一个clone函数不是一件容易的事情,尤其出现引用类型嵌套的时候,所以说去实现一个完全clone功能是件很费事又不讨好的活,这也就是在论坛中常说的深copy和浅copy的问题。话虽如此,如果对于前面所说的有个大概了解,相信实现也不是不可能。

 

C#中,尤其自己定义类型的时候,常常由于是用struct来定义还是用class来定义,即是定义一个值类型还是一个引用类型呢。在这本书上给了几个判定条件,如果如下几点都满足的话,建议用struct来定义为值类型,否则用class定义为引用类型。

1.这个类型是否主要为了数据存储;

2.是否只通过属性来访问对象的数据成员;

3.这个类型是否不会有子类型;

4.在程序处理的时候不会把这个类型对象通过多态来处理。

 

总而言之,在C#中,就是把底层面的数据用值类型来处理,而包含复杂操作,需要进行扩展的数据用引用类型来处理。

 

转载自:http://www.sosuo8.com/article/show.asp?id=2184

        http://dev.csdn.net/author/Knight94/24805bddd1b942da9266ad79192d1f49.html