类是我们用来构造 VB.NET 应用程序时的最基本的编程结构了。
那结构与类有什么相似之处与不同之处呢?
结构和类,
相同之处是都含有成员,包括构造函数、方法、属性、字段、常量、枚举和事件,都可以实现接口,都有共享的构造函数,都能对成员进行封装。
没错都有构造函数,那结构的构造函数是什么,结构难道也可被实例化成对象?
看这一段代码:
Module Module1 Private Structure structureA Dim Name As String Dim Age As Integer Public Sub New(ByVal _Name As String, ByVal _Age As Integer) Name = _Name Age = _Age End Sub End Structure Sub Main() Dim intA As New structureA '无参数的构造函数 Dim intB As New structureA("小明", 17) '有参数的构造函数 End SubEnd Module
怎么样,是不是很好奇,明明只定义一个带参数的构造函数,intB 的实例化我们可以明白,但是是 intA 怎么也能通过编译器检测,给实例化呢?
是不是很好奇,结构怎么也能实例化成对象,和类很象呢!
原因是,每个结构都隐式地具有 Public 无参数实例构造函数也就是 Public Sub New(),该构造函数能产生结构的默认值。所以你平时不写构造函数,也一样可以 New出一个结构来。
事实上,我们在结构类型声明中不能声明无参数的构造函数,只能声明“带参数”的构造函数。都可以用 new 来实例化。
那结构和类在内存分配上难道也是 一样的吗?当然不一样,差别可就大了。
简单来说 ,结构是值类型,而类是引用类型。因此,结构使用堆栈分配,类使用堆(托管堆)分配。
示例:
Module Module1 Private Structure structureA Dim Name As String Dim Age As Integer Public Sub New(ByVal _Name As String, ByVal _Age As Integer) Name = _Name Age = _Age End Sub End Structure Private Class classA Public sdNumber As Integer = 0 End Class Sub Main() Dim stcA As New structureA("小明", 0) '有参数的构造函数 Dim stcB As structureA = stcA stcB.Age = 17 Dim clsA As New classA Dim clsB As classA = clsA clsB.sdNumber = 1001 Console.WriteLine("stcA.Age = {0} ,stcB.Age = {1} ", stcA.Age, stcB.Age) Console.WriteLine("clsA .sdNumber = {0} ,clsB .sdNumber = {1}", clsA.sdNumber, clsB.sdNumber) Console.Read() End SubEnd Module
结果如图:
这就是值类型和引用类型的差别。
结构的实例 stcB.Age 赋值并不影响stcA.Age, 这是因为虽然它们同属于一种SHenry结构,而结构也是属于值类型的,值类型的变量存储在堆栈上的,每个变量都有自己单独的内存空间,所以互不影响。
相反,给 clsB.sdNumber 赋值 17 后; 则会影响slcA.sdNumber 变量的值,这是因为在 Classs 类中,虽然 sdNumber 变量是值类型,但它的对象是类,类是引用类型,而引用类型是存储在堆(托管堆)上,堆上存储的是对象的实际对象值。
不管后面定义多少个 class 类型的变量,只要不实例化,它们都只是在堆栈上划分各自的空间,来存储 class 对象的引用地址,而这每个不同名称的引用地址都指向同一个引用对象的实际值。所以,不管哪个 class 类型变量改变了值,它都会影响原始值。
说得更清楚一点,类作为引用类型,是存储在堆上,只能通过引用地址来访问它们,不能直接访问。
引用类型的变量总是包含该类型的值引用,或包含空引用。空引用不引用任何内容;除分配空引用外,对空引用进行的任何操作都是无效的。
引用类型的变量赋值只会创建引用的一个副本,而不是所引用的值的副本。它们实际上都是会指向同一块存储区的。
结构是直接存储在堆栈上,要么在数组中,要么在另一个类型中 。当包含结构实例的位置被销毁时,结构实例也会被销毁。值类型总是可以直接 访问。我们不能创建对值类型的引用,也不能引用已销毁的值类型实例。值类型的变量总是包含此类型的值。与引用类型不同,值类型的值不能为空引用,也不能引用派生相近程度较大的类型的对象。值类型的变量赋值会创建所赋的值的副 本,当然会新开辟一块内存区来保存值。
那它们还有什么区别没有呢?
当然有很多,比如所有的结构成员都默认为 Public,而类的变量和常量默认为 Private。其他的类成员默认为 Public;结构成员不能声明为 Protected,而类成员可以;结构过程不能处理事件,类过程可以;结构变量声明不能指定初始值、New 关键字或数组初始大小,类变量声明可以。
结构从不终止,所以公共语言运行库从不在任何结构上调用 Finalize 方法;类可由垃圾回收器终止,垃圾回收器会跟踪未完成的引用直到某个特定的实例,当检测到没有剩下的活动引用时, 垃圾回收器将在类上调用 Finalize。”
因为结构是值类型,是由系统统一管理内存,不是引用,所以不会对内存造成危害。
还有结构是不可继承的,而类可以继承。其实结构自身是从 ValueType 类隐式继承下来的。
数据类型可分为值类型和引用类型。值类型要么是堆栈分配的,要么是在结构中以内联方式分配的。引用类型是堆分配的,引用类型和值类型都是从最终的基类 Object 派生出来的。当值类型需要充当对象时 ,就在堆上分配一个包装,该包装能使值类型看上去像引用对象一样,并且将该值类型的值复制给它。该包装被加上标记,以便系统知道它包含一个值类型。这个过程程称为装箱,其反向过程程称为拆箱。装箱和拆箱能够使任何类型像对象一样进行处理。