[转]装箱(Boxing)和拆箱(Unboxing)
2009-06-01 08:43
405 查看
首先介绍装箱(Boxing
)和拆箱(
Unboxing
)这两个名词。
.Net
的类型分为两种,一种是值类型,另一种是引用类型。这两个类型的本质区别,值类型数据是分配在栈中,而引用类型数据分配在堆上。那么如果要把一个值类型数据放到堆上,就需要装箱操作;反之,把一个放在堆上的值类型数据取出来,则需要进行拆箱操作。
例如,对于如下简单的装箱和拆箱操作语句。
int
i = 123;
object
obj = i;//Boxing
if
( obj is
int
)
int
j = (int
) obj;//Unboxing
为了,更好的诠释装箱和拆箱操作,我借用
MSDN
关于“
Boxing
”的解释图,具体如下。
明白了这两名词的意思,现在说说为什么要减少装箱和拆箱操作。
原因有两个,主要是关于效率:一个就是对于堆的操作效率比较低;另一个就是对于堆上分配的内存资源,需要
GC
来回收,从而降低程序效率。
考虑到这两点因素,那么需要在程序中减少装箱和拆箱操作。
如何减少呢,涉及到这两个操作比较多的是,格式化输出操作,例如:
String.Format
,
Console.WriteLine
之类的语句。
例如:
Console.WriteLine( "Number list:{0}, {1}, {2}",
1,2,3 );
对于“
1
,
2
,
3
”来说,相当于前面的“
123
”一样,需要经过装箱和拆箱两个操作。那么如何避免呢,其实只要向
WriteLine
传递引用类型数据即可,也就是按照如下的方式。
Console.WriteLine( "Number list:{0}, {1}, {2}",
1.ToString(),2.ToString(),3.ToString() );
由于“
1.ToString()
”的结果是
String
类型,属于引用类型,因此不牵扯装箱和拆箱操作。
其次,牵扯到装箱和拆箱操作比较多的就是在集合中,例如:
ArrayList
或者
HashTable
之类。
把值类型数据放到集合中,可能会出现潜在错误。例如:
public
struct
Person
{
private
string
_Name;
public
string
Name
{
get
{ return
_Name; }
set
{ _Name = value
; }
}
public
Person( string
PersonName )
{
_Name = PersonName;
}
public
override
string
ToString()
{
return
_Name;
}
}
ArrayList arrPersons = new
ArrayList();
Person p = new
Person( "OldName" );
arrPersons.Add( p );
p = ( Person ) arrPersons[0] ;
p.Name = "NewName";
Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "OldName"
这个问题其实在前面的文章中已经讲过了。有人可能会说,是否可以按照如下的方式去修改呢。
( (Person ) arrPersons[0] ).Name = "NewName";//Can't be compiled
很不幸,如上操作不能通过编译。为什么呢,对于“
( (Person ) arrPersons[0] )
”来说,是系统用一个临时变量来接收拆箱后的值类型数据,那么由于值类型是分配在栈上,那么操作是对实体操作,可是系统不允许对一个临时值类型数据进行修改操作。
ArrayList arrPersons = new
ArrayList();
Person p = new
Person( "OldName" );
arrPersons.Add( p );
// Try to change the name
p = ( Person ) arrPersons[0] ;
p.Name = "NewName";
arrPersons.RemoveAt( 0 );//Remove old data first
arrPersons.Insert( 0, p );//Add new data
Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "NewName"
其实,这样操作会产生过多装箱和拆箱操作。那么更好的方法,可以通过接口来完成,从而减少装箱和拆箱操作。对于这个例子的接口实现应该如下。
public
interface
IPersonName
{
string
Name{ get
;set
;}
}
public
struct
Person:IPersonName
{
private
string
_Name;
public
string
Name
{
get
{ return
_Name; }
set
{ _Name = value
; }
}
public
Person( string
PersonName )
{
_Name = PersonName;
}
public
override
string
ToString()
{
return
_Name;
}
}
ArrayList arrPersons = new
ArrayList();
Person p = new
Person( "OldName" );
arrPersons.Add( p );
// Change the name
( (IPersonName)arrPersons[0] ).Name = "NewName";
Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "NewName"
很多人就问,为什么值类型不能修改,即
( (Person ) arrPersons[0] ).Name = "NewName";//Can't be compiled
而如上的接口类型就能修改呢,即
( (IPersonName)arrPersons[0] ).Name = "NewName";
这是由于产生的临时变量的类型不同,前者已经在前面进行说明了,后者由于产生的临时变量的类型为
IPersonName
,属于引用类型,那么相当于临时变量就是原对象的引用,那么对于对于它的修改会直接修改到原对象,因此是可以的。可以说这里的不同本身在于产生临时对象的类型不同,从而造成本质的区别。
通过接口来改写,这样就减少了装箱和拆箱操作,同时也保证了修改的正确性。不过要注意的是,这里接口对于的是引用类型,如果接口访问的或者返回的是值类型,那么用接口虽说能实现了,但是对于装箱和拆箱操作来说,并没有减少。
对于装箱和拆箱操作来说,基本上就讲完了,只要记住频繁装箱和拆箱操作会降低程序效率,因此在编写的时候要尽量避免。
_______________________________________________________________________
全文完
URL:http://blog.csdn.net/songxiaozhao/archive/2008/04/01/2235012.aspx
)和拆箱(
Unboxing
)这两个名词。
.Net
的类型分为两种,一种是值类型,另一种是引用类型。这两个类型的本质区别,值类型数据是分配在栈中,而引用类型数据分配在堆上。那么如果要把一个值类型数据放到堆上,就需要装箱操作;反之,把一个放在堆上的值类型数据取出来,则需要进行拆箱操作。
例如,对于如下简单的装箱和拆箱操作语句。
int
i = 123;
object
obj = i;//Boxing
if
( obj is
int
)
int
j = (int
) obj;//Unboxing
为了,更好的诠释装箱和拆箱操作,我借用
MSDN
关于“
Boxing
”的解释图,具体如下。
明白了这两名词的意思,现在说说为什么要减少装箱和拆箱操作。
原因有两个,主要是关于效率:一个就是对于堆的操作效率比较低;另一个就是对于堆上分配的内存资源,需要
GC
来回收,从而降低程序效率。
考虑到这两点因素,那么需要在程序中减少装箱和拆箱操作。
如何减少呢,涉及到这两个操作比较多的是,格式化输出操作,例如:
String.Format
,
Console.WriteLine
之类的语句。
例如:
Console.WriteLine( "Number list:{0}, {1}, {2}",
1,2,3 );
对于“
1
,
2
,
3
”来说,相当于前面的“
123
”一样,需要经过装箱和拆箱两个操作。那么如何避免呢,其实只要向
WriteLine
传递引用类型数据即可,也就是按照如下的方式。
Console.WriteLine( "Number list:{0}, {1}, {2}",
1.ToString(),2.ToString(),3.ToString() );
由于“
1.ToString()
”的结果是
String
类型,属于引用类型,因此不牵扯装箱和拆箱操作。
其次,牵扯到装箱和拆箱操作比较多的就是在集合中,例如:
ArrayList
或者
HashTable
之类。
把值类型数据放到集合中,可能会出现潜在错误。例如:
public
struct
Person
{
private
string
_Name;
public
string
Name
{
get
{ return
_Name; }
set
{ _Name = value
; }
}
public
Person( string
PersonName )
{
_Name = PersonName;
}
public
override
string
ToString()
{
return
_Name;
}
}
ArrayList arrPersons = new
ArrayList();
Person p = new
Person( "OldName" );
arrPersons.Add( p );
p = ( Person ) arrPersons[0] ;
p.Name = "NewName";
Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "OldName"
这个问题其实在前面的文章中已经讲过了。有人可能会说,是否可以按照如下的方式去修改呢。
( (Person ) arrPersons[0] ).Name = "NewName";//Can't be compiled
很不幸,如上操作不能通过编译。为什么呢,对于“
( (Person ) arrPersons[0] )
”来说,是系统用一个临时变量来接收拆箱后的值类型数据,那么由于值类型是分配在栈上,那么操作是对实体操作,可是系统不允许对一个临时值类型数据进行修改操作。
ArrayList arrPersons = new
ArrayList();
Person p = new
Person( "OldName" );
arrPersons.Add( p );
// Try to change the name
p = ( Person ) arrPersons[0] ;
p.Name = "NewName";
arrPersons.RemoveAt( 0 );//Remove old data first
arrPersons.Insert( 0, p );//Add new data
Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "NewName"
其实,这样操作会产生过多装箱和拆箱操作。那么更好的方法,可以通过接口来完成,从而减少装箱和拆箱操作。对于这个例子的接口实现应该如下。
public
interface
IPersonName
{
string
Name{ get
;set
;}
}
public
struct
Person:IPersonName
{
private
string
_Name;
public
string
Name
{
get
{ return
_Name; }
set
{ _Name = value
; }
}
public
Person( string
PersonName )
{
_Name = PersonName;
}
public
override
string
ToString()
{
return
_Name;
}
}
ArrayList arrPersons = new
ArrayList();
Person p = new
Person( "OldName" );
arrPersons.Add( p );
// Change the name
( (IPersonName)arrPersons[0] ).Name = "NewName";
Debug.WriteLine( ( (Person ) arrPersons[0] ).Name );//It's "NewName"
很多人就问,为什么值类型不能修改,即
( (Person ) arrPersons[0] ).Name = "NewName";//Can't be compiled
而如上的接口类型就能修改呢,即
( (IPersonName)arrPersons[0] ).Name = "NewName";
这是由于产生的临时变量的类型不同,前者已经在前面进行说明了,后者由于产生的临时变量的类型为
IPersonName
,属于引用类型,那么相当于临时变量就是原对象的引用,那么对于对于它的修改会直接修改到原对象,因此是可以的。可以说这里的不同本身在于产生临时对象的类型不同,从而造成本质的区别。
通过接口来改写,这样就减少了装箱和拆箱操作,同时也保证了修改的正确性。不过要注意的是,这里接口对于的是引用类型,如果接口访问的或者返回的是值类型,那么用接口虽说能实现了,但是对于装箱和拆箱操作来说,并没有减少。
对于装箱和拆箱操作来说,基本上就讲完了,只要记住频繁装箱和拆箱操作会降低程序效率,因此在编写的时候要尽量避免。
_______________________________________________________________________
全文完
URL:http://blog.csdn.net/songxiaozhao/archive/2008/04/01/2235012.aspx
相关文章推荐
- C#中的“装箱”(boxing)与“拆箱”(unboxing)
- C#中的“装箱”(boxing)与“拆箱”(unboxing)
- C#“装箱”(boxing)与“拆箱”(unboxing)
- C#—装箱(boxing)与拆箱(unboxing)
- C#中装箱和拆箱的作用 the effection of boxing and unboxing
- 自动装箱(boxing)和自动拆箱(unboxing)
- C#学习日记18----装箱(boxing)转换与拆箱(unboxing)转换
- C#“装箱”(boxing)与“拆箱”(unboxing)
- 减少装箱(Boxing)和拆箱(Unboxing)操作(感觉有用,抄过来的)
- vc.net中的“装箱”(boxing)与“拆箱”(unboxing)
- 装箱(Boxing)和拆箱(Unboxing)
- 装箱(Boxing)拆箱(unboxing)
- C#——装箱(boxing)和拆箱(unboxing)
- 自动装箱(boxing)和自动拆箱(unboxing)
- C#中的“装箱”(boxing)与“拆箱”(unboxing) (转载)
- 《Effective C#》Item 17:减少装箱(Boxing)和拆箱(Unboxing)操作
- C#“装箱”(boxing)与“拆箱”(unboxing)
- C#中的“装箱”(boxing)与“拆箱”(unboxing)_C#教程
- 装箱(Boxing)和拆箱(Unboxing)
- Effective C# 减少装箱(Boxing)和拆箱(Unboxing)操作