C#中值类型和引用类型(value type&Reference tyoe)深度探讨
在C#中,no matter what language the code is programmed with, 所有的source code都被编译成为IL,然后再CLR(类似于JAVA的虚拟机)上运行。当然,CLR会负责把IL转化为machine language.
但是,不管我们运用那种语言编程,都会存在一些最为常用的数据类型,我们称之为primitive type,比如int, char,float,double等等。实际上这些primitive type在CLR中,都有对应的类型,我们称之为valueType。与valueType相对应的就是 reference type了。
在CLR中,如何将这种primitive type转化为CLR type呢?我们知道,没种编程语言都有其内建的或者primitive types。 实际上这个工作是由编译器来进行map的(it's the job of the compiler to map these built-in types to CLR types).在CLR中都提供了相应的标准类型,例如Sytem.Int32-->int, System.Int16-->short等等。比如所有的数值类型和boolean类型都是value types,但是值得注意的是, System.String is a reference type. That Stated, in the CLR, System.String objects are immutable and cannot be changed after they are created. This makes System.String act much more like a value type than a reference type. System.String是引用类型的。
所有valuetypd在其元数据描述中,都是密封的,也就是说,所有valuetype不能作为基类型而派生出其他valueType类型,也不允许在valueType中存在abstract method.
在C#中,我们可以通过struct 和enum来自定义valueType 。
例如,我们定义一个struct 如下:
public struct Size
{
public int x;
public int y;
public void Show()
{
console.write("{0},{1}",x,y);
}
}
当然,类似的我们也可以定义一个类
public class CSize
{
public int x;
public int y;
public void Show()
{
console.write("{0},{1}",x,y);
}
}
很显然,Size是valueType的, 而CSize是reference type的。 那么其在内存分配时有何不同呢?请看下面例子:
public static void main()
{
Size v;
CSize r=new CSize();
v.x=1;
v.y=2;
v.show();
r.x=1;
r.y=2;
r.show();
}
上例中,v的空间位于Stack区,而r的intance位于Heap区,而r位于Stack, r指向其位于Heap的instance.
如下图所示:
请看下段程序
public static void main()
{
Size v1;
Size v2;
v1.x=2;
v1.y=3;
v2=v1;
CSize r1=new CSize();
CSize r2=r1;
}
在这段程序中, v1和v2都是值类型的,因此,v1和v2都是位于stack,并且指向不同的地址空间,虽然其值是一样的。但是 r1和r2都是位于stack的变量,但是r1和r2都指向相同的Heap地址。
如下图所示
说明:v和v1都是值类型,因此其都位于stack区,分布在不同的地址。
r1和r2都是reference type。因此,实际上其指向了Heap区中相同的instance.当然,r1和r2在Stack中还是要占用空间的,就相当于两个指针,指向相同的Heap地址。
但是,不管我们运用那种语言编程,都会存在一些最为常用的数据类型,我们称之为primitive type,比如int, char,float,double等等。实际上这些primitive type在CLR中,都有对应的类型,我们称之为valueType。与valueType相对应的就是 reference type了。
在CLR中,如何将这种primitive type转化为CLR type呢?我们知道,没种编程语言都有其内建的或者primitive types。 实际上这个工作是由编译器来进行map的(it's the job of the compiler to map these built-in types to CLR types).在CLR中都提供了相应的标准类型,例如Sytem.Int32-->int, System.Int16-->short等等。比如所有的数值类型和boolean类型都是value types,但是值得注意的是, System.String is a reference type. That Stated, in the CLR, System.String objects are immutable and cannot be changed after they are created. This makes System.String act much more like a value type than a reference type. System.String是引用类型的。
所有valuetypd在其元数据描述中,都是密封的,也就是说,所有valuetype不能作为基类型而派生出其他valueType类型,也不允许在valueType中存在abstract method.
在C#中,我们可以通过struct 和enum来自定义valueType 。
例如,我们定义一个struct 如下:
public struct Size
{
public int x;
public int y;
public void Show()
{
console.write("{0},{1}",x,y);
}
}
当然,类似的我们也可以定义一个类
public class CSize
{
public int x;
public int y;
public void Show()
{
console.write("{0},{1}",x,y);
}
}
很显然,Size是valueType的, 而CSize是reference type的。 那么其在内存分配时有何不同呢?请看下面例子:
public static void main()
{
Size v;
CSize r=new CSize();
v.x=1;
v.y=2;
v.show();
r.x=1;
r.y=2;
r.show();
}
上例中,v的空间位于Stack区,而r的intance位于Heap区,而r位于Stack, r指向其位于Heap的instance.
如下图所示:
请看下段程序
public static void main()
{
Size v1;
Size v2;
v1.x=2;
v1.y=3;
v2=v1;
CSize r1=new CSize();
CSize r2=r1;
}
在这段程序中, v1和v2都是值类型的,因此,v1和v2都是位于stack,并且指向不同的地址空间,虽然其值是一样的。但是 r1和r2都是位于stack的变量,但是r1和r2都指向相同的Heap地址。
如下图所示
说明:v和v1都是值类型,因此其都位于stack区,分布在不同的地址。
r1和r2都是reference type。因此,实际上其指向了Heap区中相同的instance.当然,r1和r2在Stack中还是要占用空间的,就相当于两个指针,指向相同的Heap地址。