网站做营销推广,安卓是哪个公司开发的,聚美优品网站建设主题,专业的郑州网站推广C#类型基础Part1-值类型与引用类型 参考资料前言值类型引用类型装箱和拆箱 参考资料
《.NET之美–.NET关键技术深入与解析》
前言
C#中的类型一共分为两类#xff0c;一类是值类型#xff08;Value Type#xff09;,一类是引用类型#xff08;Reference Type#xff09… C#类型基础Part1-值类型与引用类型 参考资料前言值类型引用类型装箱和拆箱 参考资料
《.NET之美–.NET关键技术深入与解析》
前言
C#中的类型一共分为两类一类是值类型Value Type,一类是引用类型Reference Type。值类型和引用类型是以它们在计算机内存中是如何被分配来划分的。值类型包括了 结构 和 枚举引用类型则包括了 类、接口、委托 等。还有一种特殊的值类型称为简单类型比如byte、int等这些简单类型实际上是BCL基类库类型的别名。比如声明一个int类型实际上是声明一个 System.Int32 结构类型。因此在 Int32 类型中定义的方法或属性都可以在int类型上调用比如
123.Equals(2);所有的值类型都隐式地继承自 System.ValueType 类型注意 System.ValueType 本身是一个类类型。之所以说是“隐式地”是因为在C#代码中是看不到这个继承关系的这个关系只有通过MSIL代码才能看到。 System.ValueType 类型和所有的引用类型都继承自 System.Object 基类。
C#不支持多重继承因为结构已经隐式地继承自ValueType所以结构不支持继承
说明 栈stack是一种 后进先出的数据结构在内存中变量会被分配在栈上来进行操作。堆heap是用于为引用类型的实例对象分配空间的内存区域在堆上创建一个对象会将对象的地址传给栈上的变量反过来叫变量指向此对象或者变量引用此对象。
值类型
当声明一个值类型的变量的时候变量本身包含了值类型的全部字段该变量会被分配在线程堆栈Thread Stack上。
假如有下面这个值类型代表了直线上的一点
public struct ValPoint{public int x;public ValPoint(int x){this.xx;}
}当在程序中声明一个变量
ValPoint vPoint1;
vPoint1.x10;
Console.WriteLine(vPoint1.x);//输出10上面代码中因为变量已经包含了值类型的所有字段所以已经可以进行操作并且只有对变量进行操作vPoint1.x10才会进行入栈。对变量进行操作实际上是一系列入栈、出栈操作。 如果将ValPoint改为引用类型class则会出现编译错误使用了未赋值的局部变量“vPoint1”。除此之外引用类型在运行时经常会抛出NullReferenceException异常。
如果不对vPoint1.x进行赋值直接写Console.WriteLine(vPoint1.x)则会出现编译错误使用了可能为赋值的字段x。这是因为.NET的一个约束所有的元素使用前都必须初始化。比如下面语句也会引发这个错误:
int i;
Console.WriteLine(i);虽然结构类型变量本身不需要像类一样使用new操作符创建一个实例其本身就相当于一个实例但如果要使用它的内部成员则要在使用前对它进行赋值。结构还有一个特性调用结构上的方法前需要对其所有字段进行赋值。 修改ValPoint
public struct ValPoint{public int x;public void Blank(){}
}那么下面的代码将会发生编译错误
ValPoint vPoint1;
vPoint1.Blank();//使用了未赋值的变量vPoint1
Console.WriteLine(vPoint1);//使用了未赋值的变量vPoint1解决上述问题可以通过这样一种方式编译器隐式地为结构类型创建无参数的构造函数。在这个构造函数中会对结构成员进行初始化所有的值类型成员被赋予0或者相当于0的值所有的引用类型被赋予null值。因此Struct类型不可以自行声明无参数的构造函数。所以可以通过隐式声明的构造函数去创建一个ValPoint类型变量
ValPoint vPoint1new ValPoint();
Console.WriteLine(vPoint1.x);//输出0引用类型
当声明一个引用类型变量并使用new操作符创建引用类型实例的时候该引用类型的变量会被分配到线程栈上变量保存了位于堆上的引用类型的实例的内存地址。变量本身不包含任何类型所定义的数据。如果仅仅声明一个变量但不使用new操作符由于在堆上还没有创建类型的实例因此变量值为null即不指向任何对象。
如果有这样一个类它依然代表直线上的一点
public class RefPoint{public int x;public RefPoint(int x){this.xx}public RefPoint(){}
}当仅仅写下
RefPoint rPoint1;它会在线程栈上创建一个不包含任何数据也不指向任何对象不包含内存地址的变量。 而当使用new操作符时
rPoint1new RefPoint(1)则会完成下面几件事
在应用程序堆上创建一个引用类型对象的实例并分配内存地址自动传递该实例的引用给构造函数。正因为如此才可以使用this来访问这个实例调用该类型的构造函数。返回该实例的引用内存地址赋值给人Point1变量
装箱和拆箱
简单来说装箱就是将一个值类型转换为等价的引用类型。它的过程分为这样几步
在堆上为新生成的对象实例分配内存。该对象实例包含数据但没有名称。将栈上值类型变量的值复制到堆上的对象中。将堆上创建的对象的地址返回给引用类型变量。
装箱实例代码如下
int i1;
Object boxedi;
Console.WriteLine(Boxed Point:boxed);拆箱则是将一个已装箱的引用类型转换为值类型
int i1;
Object boxedi;
int j;
j(int)boxed;
Console.WriteLine(UnBoxed Point: j);需要注意的是拆箱操作需要显示声明拆箱后转换的类型。它分为两步来完成
获取已装箱的对象的地址。将值从堆上的对象中复制到堆栈上的值变量中。
可见装箱和拆箱需要反复在堆上进行操作因此在程序中应该尽量避免无意义的装箱和拆箱。