Struct与Class的异同?本是一个老生常谈话题,前几天看帖就看到了Struct 与Class辨析,其中也提到了《[你必须知道的.NET] 第四回:后来居上:class和struct》(虽然在园子里看了这个系列,但仍然买了本书看),回帖也特别热闹。我也躺下这个浑水!希望能给您带来不一样的视觉,欢迎评论。本文主题如下:
Struct与Class的异同,到底什么是什么呢?首先来两段代码,给个直观印象。
以下是Struct代码:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace StructVSClass
- {
- public struct Person
- {
- //fields
- public string _Name;
- public int _Age;
- //constructors
- public Person(string name, int age)
- {
- _Name = name;
- _Age = age;
- }
- //public Person()//struct can't declare default constructor ,which has no parameters
- //{ }
- //properties
- public string Name
- {
- get { return _Name; }
- set { _Name = value; }
- }
- public int Age
- {
- get { return _Age; }
- set { _Age = value; }
- }
- //methods
- public void Print()
- {
- System.Console.WriteLine("Person Name: " + _Name + "\tPerson Age: " + _Age);
- }
- //indexers,operators and even other structure types
- }
- class Program
- {
- static void Main(string[] args)
- {
- Person p0 = new Person();//using the default constructor.
- p0.Print();
- Person p1 = new Person("skynet", 23);//using our custom constructor.
- p1.Print();
- Person p2;//Declare a struct object without "new."
- p2._Name = "wuqin";
- p2._Age = 23;
- p2.Print();
- /*
- Person p3;
- p3.Name = "abc";//Compile error,because if you use properties or methods ,you have to declare a struct with new.
- p3.Age=23;
- p3.Print();
- */
- }
- }
- }
以下是class代码:
- Classusing System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace StructVSClass
- {
- public class Person
- {
- //fields
- public string _Name;
- public int _Age;
- //constructors
- public Person()
- {
- }
- public Person(string name, int age)
- {
- _Name = name;
- _Age = age;
- }
- //properties
- public string Name
- {
- get { return _Name; }
- set { _Name = value; }
- }
- public int Age
- {
- get { return _Age; }
- set { _Age = value; }
- }
- //methods
- public void Print()
- {
- System.Console.WriteLine("Person Name: " + _Name + "\tPerson Age: " + _Age);
- }
- //indexers,operators and even other structure types
- }
- class Program
- {
- static void Main(string[] args)
- {
- Person p0 = new Person();//using the default constructor.
- p0.Print();
- Person p1 = new Person("skynet", 23);//using our custom constructor.
- p1.Print();
- //Person p2;//compile error.Declare a class object must use new operator.
- //p2._Name = "wuqin";
- //p2._Age = 23;
- //p2.Print();
- }
- }
- }
有没有感觉Struct跟Class很像呢。他们基本上包含同样的成员,下面列出了可在类或结构中声明的所有不同种类的成员:字段、常量、属性、方法、构造函数、析构函数、事件、索引器、运算符、嵌套类型。
从上面的代码可以知道:以下几点:
我们现在能看到的就这么多。他们之间就这么点区别吗?什么造成了这些区别呢?
首先我们考虑以下事实:
那他们的异同是否可以归结为值类型与应用类型的差异呢?且听一一道来。
值类型:
引用类型:
关于堆栈与托管堆的简单说明:
Windows使用一个系统——虚拟寻址系统,该系统把程序可用的内存地址映射到硬件内存的实际地址上,这些任务完全由Windows在后台完成。这样在32位的处理器上每个程序可以拥有4GB(=2^32bit)内存(64位的处理器上每个程序可以拥有8GB(=2^64bit)内存),而无论计算机上有多少硬盘空间,这个4GB称为虚拟地址空间或虚拟内存。
这个4GB内存实际上包含了程序的所有部分,包含可执行代码、代码加载的所有DLL、以及程序运行时使用的所有变量的内容。4GB的每个存储单元都是从0开始往上排序的,要访问存储在其中的某个空间的一个值,就需要提供表示该存储单元的数字。
在进程的这个4GB空间可分为多个区域,其中有两个区域:线程堆栈(thread stack)和托管堆(managed heap)。线程的堆栈比托管堆要小很多。
- 线程堆栈:是一个先进后出的结构,存储对象成员的数据。在调用方法使,也使用堆栈存储传递给方法的所有参数的副本。我们不知道堆栈在地址空间的什么地方,这些信息在进行C#开发的时候也不需要知道。堆栈是自上向下填充的,即从高内存地址向低内存地址填充。
- 托管堆:另一个存储内存区域,跟堆栈不同它在GC的控制下工作。托管堆是自下向上填充的,这与堆栈是不一样的。
了解更多,请查阅相关资料。
我认为正是由于值类型与引用类型的上述特性,造成了Struct与Class的一系列差异:
IDisposable
接口,所以你还是可以使用dispose模式的(有点曲线救国的味道)。值类型继承自System.ValueType,而他又继承自System.Object。任何继承自System.ValueType的类型CLR都认为是值类型。引用类型直接或间接继承自System.Object,但继承链中不能包含System.ValueType。
所以虽说值类型不能继承类,但System.Object是一个特例。Struct可以调用System.Object中的所有方法,甚至是重写。特别需要注意的是System.ValueType只重写了System.Object的Equals方法和GetHashCode方法,没有添加任何新的东西。因此,Struct调用Equals方法时比较的是struct中字段是否相等,而Class执行的是比较两个引用指向的是否是同一个对象(除非类或其祖先类重写了Equals方法)。
Class不能继承Struct可以说是,因为他们的在内存中的存储结构不一样。但为什么Struct不能继承Struct呢?我想着大概是因为:Struct在内存中只存储它的字段数据值,没有方法表指针,因此实现不了多态(polymorphism),不能正确决定方法的调用。正是由于这点,如果没有运行时多态,继承是一个不完整的面向对象,所以所有的从System.ValueType继承的值类型都被标记为sealed。
对于一个引用类型,ReadOnly阻止你重新分配一个引用指向其他对象。但是它不阻止你修改引用对象的值。对于值类型,ReadOnly就像C++中的const一样,它阻止你修改对象的值。这意味着你不能再重新分配,因为这将导致所有的字段重新初始化。下面的代码证明了这个:
- class MyReferenceType
- {
- int state;
- public int State
- {
- get
- {
- return state;
- }
- set
- {
- state = value;
- }
- }
- }
- struct MyValueType
- {
- int state;
- public int State
- {
- get
- {
- return state;
- }
- set
- {
- state = value;
- }
- }
- }
- class Program
- {
- readonly MyReferenceType myReferenceType = new MyReferenceType();
- readonly MyValueType myValueType = new MyValueType();
- public void SomeMethod()
- {
- myReferenceType = new MyReferenceType(); // Compiler Error
- myReferenceType.State = 1234; // Ok
- myValueType = new MyValueType(); // Compiler Error
- myValueType.State = 1234; // Compiler Error
- }
- }
注意:在foreach语句声明的变量和using语言隐式的表示ReadOnly,因此如果您使用的是结构体,您将无法改变其状态。
上面讲了那么多了,现在我们总结一下。class和struct是 .NET Framework 中的通用类型系统的两种基本构造。两者在本质上都属于数据结构,封装着一组整体作为一个逻辑单位的数据和行为。数据和行为是该类或结构的“成员”,它们包含各自的方法、属性和事件等。
class或struct的声明类似于蓝图,用于在运行时创建实例或对象。如果定义一个名为 Person 的class或struct,则 Person 为类型名称。如果声明并初始化 Person 类型的变量 p,则 p 称为 Person 的对象或实例。可以创建同一 Person 类型的多个实例,每个实例在其属性和字段中具有不同的值。
class是一种引用类型。创建class的对象时,对象赋值到的变量只保存对该内存的引用。将对象引用赋给新变量时,新变量引用的是原始对象。通过一个变量做出的更改将反映在另一个变量中,因为两者引用同一数据。
struct是一种值类型。创建struct时,struct赋值到的变量保存该结构的实际数据。将结构赋给新变量时,将复制该结构。因此,新变量和原始变量包含同一数据的两个不同的副本。对一个副本的更改不影响另一个副本。
类通常用于对较为复杂的行为建模,或对要在创建类对象后进行修改的数据建模。结构最适合一些小型数据结构,这些数据结构包含的数据以创建结构后不修改的数据为主。
何时该用struct、何时该用class: