初学C#,知道一句话:“一切都是对象”。这是C#与C++一个很大的区别。同时,对于引用类型的对象来说,赋值操作传递的是一个引用,而不是值。初学时,为了切换到C#的频道,想当然的认为所有的赋值操作均是如此,但是这样想有时会造成严重的错误。原因在于,C#中,除了引用类型,还存在一类很重要的类型,实际上也是所有程序员都很熟悉的类型:值类型。
值类型与引用类型的最大区别在与内存分配,一般而言值类型的变量,是在栈上进行分配,引用类型,在托管堆分配。
一、赋值操作
我们来看一个例子:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;namespace ConsoleApplication1
{
class Program
{
public class Person
{
public string Name;public override string ToString()
{
return Name;
}
}static void Main(string[] args)
{
ArrayList members = new ArrayList();
Person p = new Person();
p.Name = "Old name";
members.Add(p); // [A]Person p2 = ((Person)members[0]); // [B]
p2.Name = "New Name";Console.WriteLine("Value is: " + members[0].ToString());
}
}
}
输出是什么?
由于C#中,“对象赋值是传递实例的引用”,因此,上面[B]处的代码,实际上是取得了数组中的第一个元素,类似C++中得到了第一个元素的地址,后面的操作实际上是直接操作了数组中第一个元素,因此输出会是修改后的值“New Name”。
现在,我们把“Person” 的定义更改为结构。看看输出发生了什么变化。我们发现,输出的仍是初始化时的值。
这是为什么?
这就是引用类型与值类型的不同。“对象赋值是传递实例的引用”,这句话只针对引用类型。对于值类型来说,赋值操作,就是数据拷贝。当Person是一个结构时,在[A]处,进行了装箱(Boxing),在[B]处,进行了拆箱(Unboxing),变量 p2 是一个不同的对象,是数组第一个元素的完整拷贝。
综合以上考虑,微软在C#编程向导中规定,尽可能不要使用Struct,除非要定义的结构满足下面的特征:
- 它在逻辑上表示单个值。
- 它的实例大小小于 16 字节。
- 它是不可变的。
- 它不必频繁被装箱。
二、this指针
准确的说应该是this对象。从语义上讲,C++的this与C#一样,都指向该类的当前实例。不同的是,C++的this是一个指针,C#没有指针。另外,C++中,一般情况下this指针不允许在构造函数中使用,因为此时,对象尚未创建完成。C#则可以,可以理解为,当构造函数进入时,类的实例已经创建了,this有效。这也是为什么我们可以再C#的构造函数中使用this指针来初始化类成员。
三、构造函数
构造函数。C#中的结构和类在处理默认构造函数时有较大区别。区别就是,struct总是有一个系统默认的构造函数,并且不允许自定义构造函数。原因是必须保证值类型的初始化状态。
Hi Paitum,
Structs and constructors behave a bit differently from classes. In classes,
an instance must be created by calling new before the object is used; if
new isn't called, there will be no created instance, and the reference must
be null.There is no reference associated with a struct, however. If new isn't
called on the struct, an instance that has all of its fields zeroed is
created. In some cases, user can then use this instance without further
initialization.It is therefore important to make sure that the all-zeroed state is a valid
initial state for all the value types.A default(parameterless) constructor for a struct could set different
values than the all-zeroed state which would be unexpected behavior. The
.Net Runtime therefore prohabits default constructors for struct.Best Regards,
Billy Zhang
Microsoft
class 没有这个限制,理论上讲,我们不需要像C++那样为每一个类写默认无参构造函数,因为.NET会为你写一个,这里有一个例外,就是当你自己写了一个带参数的构造函数后,编译器就不再为你写无参构造函数。所以,这里有一个衍生规则:任何情况下,都自己写一个默认构造函数。
Thanks:
1. 理解C#值类型与引用类型
2. Structs cannot contain explicit parameterless constructors. WHY?
3. Effective C#: 50 Specific Ways to Improve Your C#
4. Structure Design Guidelines, Value Type Usage Guidelines, in MSDN.