Introduce DataBase,Asp.net,JavaScript,Xml,Html,Css,Sql,Php,ASP.NET Controls,AJAX,Tools,HTML,CSS,JavaScript,Open Source Project,WPF,.Net Framework,Linq
Top Recommended Hosting

Boxing and Unboxing

by the3factory 3/12/2008 6:50:00 AM

Value types are containers for data. They are not polymorphic types. On the other hand, the .NET Framework was designed with a single reference type, System.Object, at the root of the entire object hierarchy. These two goals are at odds. The .NET Framework uses boxing and unboxing to bridge the gap between these two goals. Boxing places a value type in an untyped reference object to allow the value type to be used where a reference type is expected. Unboxing extracts a copy of that value type from the box. Boxing and unboxing are necessary for you to use value types where the System.Object type is expected. But boxing and unboxing are always performance-robbing operations. Sometimes, when boxing and unboxing also create temporary copies of objects, it can lead to subtle bugs in your programs. Avoid boxing and unboxing when possible.

Boxing converts a value type to a reference type. A new reference object, the box, is allocated on the heap, and a copy of the value type is stored inside that reference object. See Figure 1 for an illustration of how the boxed object is stored and accessed. The box contains the copy of the value type object and duplicates the interfaces implemented by the boxed value type. When you need to retrieve anything from the box, a copy of the value type gets created and returned. That's the key concept of boxing and unboxing: A copy of the object goes in the box, and another gets created whenever you access what's in the box.
Figure 1 Value type in a box. To convert a value type into a System.Object reference, an unnamed reference type is created. The value type is stored inline inside the unnamed reference type. All methods that access the value type are passed through the box to the stored value type.
 

The insidious problem with boxingand unboxing is that it happens automatically. The compiler generates the boxing and unboxing statements whenever you use a value type where a reference type, such as System.Object is expected. In addition, the boxing and unboxing operations occur when you use a value type through an interface pointer. You get no warningsboxing just happens. Even a simple statement such as this performs boxing:

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

The referenced overload of Console.WriteLine takes an array of System.Object references. Ints are value types and must be boxed so that they can be passed to this overload of the WriteLine method. The only way to coerce the three integer arguments into System.Object is to box them. In addition, inside WriteLine, code reaches inside the box to call the ToString() method of the object in the box. In a sense, you have generated this construct:

int i =25;
object o = i; // box
Console.WriteLine(o.ToString());

Inside WriteLine, the following code executes:

object o;
int i = ( int )o; // unbox
string output = i.ToString( );

You would never write this code yourself. However, by letting the compiler automatically convert from a specific value type to System.Object, you did let it happen. The compiler was just trying to help you. It wants you to succeed. It happily generates the boxing and unboxing statements necessary to convert any value type into an instance of System.Object. To avoid this particular penalty, you should convertyour types tostring instances yourself before you send them to WriteLine:

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

This code uses the known type of integer, and value types (integers) are never implicitly converted to System.Object. This common example illustrates the first rule to avoid boxing: Watch for implicit conversions to System.Object. Value types should not be substituted for System. Object if you can avoid it.

Another common case in which you might inadvertently substitute a value type for System.Object is when you place value types in .NET 1.x collections. This incarnation of the .NET Framework collections store references to System.Object instances. Anytime you add a value type to acollection, it goes in a box. Anytime you remove an object from a collection, it gets copied from the box. Taking an object out of the box always makes a copy. That introduces some subtle bugs in your application. The compiler does not help you find these bugs. It's all because of boxing. Start with a simple structure that lets you modify one of its fields, and put some of those objects in a collection:

public struct Person
{
private string _Name;
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
}
}
public override string ToString( )
{
Return _Name;
}
}
// Using the Person in a collection:
ArrayList attendees = new ArrayList( );
Person p = new Person( "Old Name" );
attendees.Add( p );
// Try to change the name:
// Would work if Person was a reference type.
Person p2 = (( Person )attendees[ 0 ] );
p2.Name = "New Name";
// Writes "Old Name":
Console.WriteLine(
attendees[ 0 ].ToString( ));

Person is a value type; it gets placed in a box before being stored in the ArrayList. That makes a copy. Then another copy gets made when you remove the Person object to access the Name property to change. All you did was change the copy. In fact, a third copy was made to call the ToString() function through the attendees[0] object.

For this and many other reasons, you should create immutable value types . If you must have a mutable value type in a collection, use the System.Array class, which is type safe.

If an array is not the proper collection, you can fix this error in C# 1.x by using interfaces. By coding to interfaces rather than the type's public methods, you can reach inside the box to make the change to the values:

public interface IPersonName
{
string Name
{
get; set;
}
}
struct Person : IPersonName
{
private string _Name;
public string Name
{
get
{
return _Name;
}
set
{
_Name = value;
}
}
public override string ToString( )
{
return _Name;
}
}
// Using the Person in a collection:
ArrayList attendees = new ArrayList( );
Person p = new Person( "Old Name" );
attendees.Add( p ); // box
// Try to change the name:
// Use the interface, not the type.
// No Unbox needed
(( IPersonName )attendees[ 0 ] ).Name = "New Name";
// Writes "New Name":
Console.WriteLine(
attendees[ 0 ].ToString( )); // unbox

The box reference typeimplements all the interfaces implemented by the original object. That means no copy is made, but you call the IPersonName.Name method on the box, which forwards the request to the boxed value type. Creating interfaces on your value types enables you to reach inside the box to change the value stored in the collection. Implementing an interface is not really treating a value type polymorphically, which reintroduces the boxing penalty .

Many of these limitations change with the introduction of generics in C# 2.0 . Generic interfaces and generic collections will address the both the collection and the interface situations. Until then, though, avoid boxing. Yes, value types can be converted to System.Object or any interface reference. That conversion happens implicitly, complicating the task of finding them. Those are the rules of the environment and the language. The boxing and unboxing operations make copies where you might not expect. That causes bugs. There is also a performance cost to treating value types polymorphically. Be on the lookout for any constructs that convert value types to either System.Object or interface types: placing values in collections, calling methods defined in System.Object, and casts to System.Object. Avoid these whenever you can.

Related posts

Sign up for PayPal and start accepting credit card payments instantly.


Powered by BlogEngine.NET 1.2.0.0