代码改变世界

Effective C# 学习笔记(四十五)减少装箱拆箱行为

2011-08-06 10:35  小郝(Kaibo Hao)  阅读(591)  评论(0编辑  收藏  举报

值类型用来存储数据,引用类型用来表现多态,而所有.net framework中的类型都继承自System.Object。这看起来有些冲突。.NET framework 用装箱和拆箱来解决两类类型间的转换。但是这种操作是十分消耗资源且影响性能的。

装箱:把一个值类型放入一个未命名的引用类型中,以使得该类型可被当作引用类型使用。

拆箱:从一个引用类型中将值类型的Copy取出。

可以通过范型来避免装箱拆箱,但是.net中还是有许多方法是不支持范型方法的,所以这些方法的调用,在对待值类型时会引起自动的装箱拆箱行为。如下代码所示:

Console.WriteLine("A few numbers:{0}, {1}, {2}", 25, 32, 50);

上面的方法原来的参数列表是这样的Console.WriteLine(string,object[]),所以上面的253250三个int的值类型对象对自动进行装箱。其实在其内部的执行方法是这样的:

private static void SampleThree()

{

object firstParm = 5;//装箱

object o = firstParm;

int i = (int)o; // unbox

string output = i.ToString();

}

 

避免装箱拆箱的方法应该这样写:

Console.WriteLine("A few numbers:{0}, {1}, {2}", 25.ToString(), 32.ToString(), 50.ToString());

 

.NET framework 的集合类型存储的System.Object的引用类型,所以对于集合类型的范型,在传入值类型的类型参数时,会产生装箱操作。而当你讲一个对象从集合中取出,你就会从集合中得到该对象的一个拷贝。但这常常会引起一个bug。看如下带代码:

public struct Person

{

public string Name { get; set; }

public override string ToString()

{

return Name;

}

}

// Using the Person in a collection:

var attendees = new List<Person>();

Person p = new Person { Name = "Old Name" };

attendees.Add(p);

// Try to change the name:

// Would work if Person was a reference type.

Person p2 = attendees[0];

p2.Name = "New Name";

// Writes "Old Name":

Console.WriteLine(attendees[0].ToString( ));

 

如上面的代码所示:Person类型是个值类型,在List<Person>类型的对象时你只修改的了该对象的一个Copy,即一个根据原对象新创建的对象,而没有修改集合中的值。这时你应该使用immutable值类型。如第20次笔记所说。