不可不知的C#基础–从 struct 和 class的异同 说开去
我知道很多人都讨论过这个问题, 多我一个不多, 少我一个不少.
最近有人又问到这个问题, 所以想再说说. 万丈高楼平地起, 地基很重要. 懂了的人不要嫌罗嗦, 欢迎补充或者纠正.
从起源/定义说起
Struct 从C 的时代就已经有了(向丹尼斯.里奇致敬), 它是Structure 的缩写 -- 就是结构的意思. 它是一种最初级的数据结构, 它包含一到多个相同类型或不同类型的值或者变量. 它就像是一个存储数据的"包".
Class(类) 是有了面向对象概念之后才有的, 它"是创建对象的蓝图,描述了所创建的对象共同的属性和方法".
从它们被创立的用途可以看出Class 比Struct 负担了更大的使命.
类从出生的那天就被赋予了一个伟大的使命: 模拟真实世界的行为, 拥有继承和多态两种利器.
Struct 到了C#这个领域中有了一个进化, 它甚至可以实现接口(当然,这个也是C时代没有的东西), 在本文中会有一个简单的介绍.
值类型 VS 引用类型
Struct 是属于值类型这个阵容, 而所有class 都是引用类型.
这意味着什么?
当我们使用等号"赋值"时, 对于Struct而言就是将同样的值复制给另一个变量; 而对于Class而言就只是将它们的名字指向同一个对象.
看下面的实例:
class FooClass { public int FooValue; } struct FooStruct { public int FooValue; } class Program { static void Main(string[] args) { FooClass classObj = new FooClass(); classObj.FooValue = 0; FooClass classObj2 = classObj; classObj2.FooValue = 1; FooStruct structObj = new FooStruct(); structObj.FooValue = 0; FooStruct structObj2 = structObj; structObj2.FooValue = 1; } }
Struct实现接口
前面提到了Struct可以实现接口, 下面我们引用一个实例:
interface IPromotion { void promote(); } struct Employee : IPromotion { public string Name; public int JobGrade; public void promote() { JobGrade++; } public Employee(string name, int jobGrade) { this.Name = name; this.JobGrade = jobGrade; } public override string ToString() { return string.Format("{0} ({1})", Name, JobGrade); } } class Program { static void Main(string[] args) { Employee employee = new Employee("Cool Guy", 65); IPromotion p = employee; Console.WriteLine(employee); p.promote(); Console.WriteLine(employee); } }
不同的用途
使用中在struct 和 class 两者间该选谁?
我们在编程中要实现某种数据结构时, 绝大部分情况下我们会选择class -- 因为它的强大和特定的使命. 但是当我们要传递或存储一些小数据结构时,可以考虑struct.
Net framework 下有很多已经定义好了的struct例如:
System.Drawing.Rectangle
System.Drawing.Color
System.Drawing.Point
使用时要记住struct的特性.
那么我再补充几点吧:
初始化方面:
值类型分配在堆栈上,变量本身包含了实例所有字段。
引用类型分配在托管堆上,被分配在托管堆上的对象都有一些与之关联的额外成员要被初始化。分配完后返回对象位于托管堆的地址。
内存释放方面:
值类型没有分配在托管堆,所以不受GC控制。一旦值类型不被引用,为它分配的存储空间就会立即释放。值类型也有Finalize方法,但被回收时,CLR不会调用该方法。
引用类型受GC控制。
继承方面:
不能把值类型当作基类,所以值类型里不要有虚方法,也没有抽象方法,所有的方法都隐含有sealed修饰符。
用途方面:
值类型不要频繁用于方法的参数传递。因为参数以传值的方式传递,导致值类型的字段频繁被拷贝。
值类型不要作为方法返回值频繁返回。原因同上。
值类型不要频繁用于ArrayList,Hashtable类。因为一不小心会频繁装箱。
值类型的装箱与拆箱可以讲很多。
就举一个例子,什么时候装箱比较好。
int a=5;
Console.WriteLine("{0},{1},{2}",a,a,a);//反例
Object o=a;//装箱
Console.WriteLine("{0},{1},{2}",o,o,o);