C/C++程序员或多或少都有使用struct的经历,在C++中struct和class的区别不大,除了默认成员的可访问性,这点在C#中则大相径庭。本文将力图说明C#中struct和class的区别以及如何正确的使用struct。java
众所周知,在java中并无struct的概念,那么C#为什么引入struct呢?最基本缘由是能够建立值类型的类型,使在托管环境中有更好的性能。程序员
区别于java,C#有值类型和引用类型的概念(java只有引用类型)。引用类型的实例分配在堆上,当对象没有被引用时被垃圾回收器回收;值类型的实例分配在栈上,当离开其做用域后内存被回收。值类型自己存储的是值自己,引用类型存储的是引用,C#语言提供的原始类型除了string类型其它都是值类型。函数
在C#中struct是值类型,class是引用类型,能够经过enum和struct关键字建立值类型的对象。使用值类型能够减小托管堆上对象的数量,从而减小垃圾回收器(GC)的负担,提升性能,值类型也有明显的缺点:经过值类型传递较大对象的开销比较昂贵、装箱和拆箱对性能形成影响。性能
public struct Employeestruct { private int _fooNumber; public Employeestruct(int fooNumber) : this() { _fooNumber = fooNumber; } public string Name { get; set; } public int Age { get; set; } public string GetMessage() { return string.Format("{0}--{1}", Name, Age); } }
从上面能够看到,struct和class很是类似,不过它们仍是有本质的区别,接下来咱们就一一分析。this
Structs从System.ValueType继承而classes继承于System.Object或从System.Object继承的其余类型,固然System.ValueType也从System.Object继承(这不是重点)。Structs能够实现接口,但不能从另外一个classes、structs继承,并且不能做为其余classes的基类。要知道,当你把structs做为接口使用时,就进行一次隐形装箱,由于接口做为引用类型。请看下面代码:spa
struct Foo : IFoo { int x; } IFoo iFoo = new Foo();
Foo的实例被建立并赋值给iFoo(装箱),当iFoo调用时实际上使用的是Foo装箱后的实例。.net
尽管CLR容许,可是不容许在structs中定义无参的构造函数。对于值类型,编译器默认状况下不生成默认的构造函数,也不调用默认的构造函数,因此C#编译器不容许使用者定义无参的构造函数。因为structs不生成默认的构造函数,因此不能初始化字段。以下(错误):code
Struct MyFoo { int x = 1; }
记住,编译器把初始化工做放在构造函数中,因为structs没有默认的构造函数,因此不能初始化字段。orm
有趣的是,你可使用下面的语法:对象
Foo foo = new Foo();
经过以前的章节了解到,尽管初始化foo使用了new操做符,可是structs的实例分配到栈上。new Foo()不会调用无参的构造函数,仅仅是初始化该struct的字段为默认值。
struct Foo { int x; public Foo(int x) { this.x = x; } } Foo foo = new Foo();
注意,我已经重载了构造函数,然而我可以调用new Foo()。
Structs不容许定义析构函数,若是你扔就去定义一个析构函数,编译器会当即提示一个错误,不过structs能够实现IDisposable接口。
Structs不能和null进行比较,这点在.net framework2.0已再也不是问题(可空类型),关于可空类型,超出本文讨论范围。
对于引用类型,readonly阻止再为变量赋值,但不阻止你修改当前引用对象的状态。对于值类型,readonly有点相似C++中的const关键字,它阻止你修改引用对象,也意味着不能再为他赋值,由于这会致使从新初始化一次。请看下面代码:
public class MyReferenceType { public int State { get; set; } } public struct MyValueType { public int State { get; set; } } public void TestMethod() { myReferenceType = new MyReferenceType(); // 错误 myReferenceType.State = 1234; // Ok myValueType = new MyValueType(); // 错误 myValueType.State = 1234; // 错误 }
foreach 语句和using语句的变量为隐式readonly,因此若是使用structs,将没法更改其状态。
经过前面的描述已经清楚classes和structs的区别,那咱们来看下适合使用structs的场景:
l 实例使用起来像C#的基元类型
l 须要建立大量的、短暂实例(例如在循环体内)
l 实例不须要大量的传递
l 不须要从其余类型继承或不但愿被其余类型继承
l 但愿被人操做你实例的副本
不适合使用structs的场景:
l 实例过大,当实例过大时,传递实例会有很大的开销。微软建议structs的理想大小应该在16bytes如下
l 当回引发装箱、拆箱操做时。关于装箱、拆箱超出本文讨论范围