views:

423

answers:

4

In short, I think boxing is an annoyance. Let's look at some alternatives...

public class Box<T> 
    where T : struct
{
    public T Value { get; set; }

    public static implicit operator T(Box<T> box)
    {
        return box.Value;
    }
}

System.Int32 derives from abstract class System.ValueType which derives from class System.Object. You cannot derive from System.ValueType in C# but I would guess that the struct keyword does exactly that and the CLI recognizes these kind of type definitions as having pass-by-value semantic. Anyhow, when a struct is assigned to type of object boxing occurs. I don't wanna get caught up in boxing per se, instead I wanna get straight to it.

I looked at some of the IL generated by the C# compiler.

object obj = 1;

.locals init ([0] object obj)
L_0000: nop 
L_0001: ldc.i4.1 
L_0002: box int32 // Convert a value type (of the type specified in valTypeToken) to a true object reference. 
L_0007: stloc.0

Found this on MSDN...

A value type has two separate representations within the Common Language Infrastructure (CLI):

  • A 'raw' form used when a value type is embedded within another object or on the stack.

  • A 'boxed' form, where the data in the value type is wrapped (boxed) into an object so it can exist as an independent entity.

This have lead me to conclude that it should be equally expensive to write code like this...

var box = obj as Box<int>;
if (box != null)
{
    Console.WriteLine(box.Value);
}

If I intend to pass that same value around as an System.Object do I really wanna unbox and box the ValueType every time? My gut feeling is telling me no, but I cant really find good motivation anyone care to comment on all this blabbering?

EDIT

Anyone ever find themselves doing this? I realize that it might look bizarre but at one point I found myself in a position were I wanted to abstract computations based of several different representations. I did it like this and with lambda expressions. Its not really related to boxing but it sort of allowed me to treat any ValueType (this struct is conveniently 8-byte aligned) as if it were one single type "ReinterpretCast".

[StructLayout(LayoutKind.Explicit)]
public struct ReinterpretCast
{
    [FieldOffset(0)] sbyte @sbyte;
    [FieldOffset(0)] byte @byte;
    [FieldOffset(0)] short @ushort;
    [FieldOffset(0)] ushort @short;
    [FieldOffset(0)] int @int;
    [FieldOffset(0)] uint @uint;
    [FieldOffset(0)] long @long;
    [FieldOffset(0)] ulong @ulong;
    [FieldOffset(0)] float @float;
    [FieldOffset(0)] double @double;
}
+2  A: 

I'm not entirely sure of your question here. Are you just asking whether your solution is perhaps better than normal boxing? It certainly has some appeal. If you're asking why boxing wasn't implemented this way in the first place, just remember that .NET didn't have generics to start with.

EDIT: Boxing is relatively rare with generics anyway. Don't forget that you'll still have to do a runtime cast if a reference to an instance of your type is passed around as object (which is usually the case anyway for boxing). Also don't forget interfaces - if a value type implements an interface, so does its corresponding reference type used for boxing. Your solution won't remove that use of boxing, as you can't make your type "pretend" to implement the interface. (You might be able to do something with the DLR, but by that time most of the point has been lost :)

Jon Skeet
Think of all the legacy stuff and frameworks like WPF where object pretty much the only type. I agree though, this probably is a thing of the past.
John Leidegren
A: 

What we think is faster is completely irrelevant. Only the profiler is relevant when considering what is faster.

JaredPar
A: 

Okay, there are several topics you're touching here. First of all, let's take a look at value types and why they exist. Value types are what you use when you need value semantics:

With classes, it is possible for two variables to reference the same object, and thus possible for operations on one variable to affect the object referenced by the other variable. With structs, the variables each have their own copy of the data (except in the case of ref and out parameter variables), and it is not possible for operations on one to affect the other. Furthermore, because structs are not reference types, it is not possible for values of a struct type to be null.

All numeric types, for example, are value types precisely because they need to have value semantics. If the variable x has value 17 and you assign x to y, then y will have its own value 17 and incrementing y won't change x to 18. Therefore, unless you have a good reason, use a struct only when defining a type that needs to have value semantics.

At the implementation level, value semantics are enforced by using in-line allocation. You can read more about it here.

This leads us to boxing. When does boxing happen? When you cast a value type into a reference type. You used the type object as an example, but with C# generics, that's something that should happen rarely in practice. A more frequent case would be to cast a value type into an interface; for example, casting a double into an IEquatable or IComparable. At any rate, if you cast a valu type into a reference type, boxing will and must occur.

What does really happen when boxing occurs? A copy of the instance to be boxed is made and placed on the heap, as an independent object, so that it can be safely referenced even when the original instance goes out of scope. If it wasn't for the boxing, it would be easy to get the CLR to try to access invalid memory and we all know that's a baaaaad thing.

So, is boxing good or bad? On the one hand it's good, because it allows you to safely cast value types into reference types when you need it. On the other hand, it creates "litter" -- short-lived instances of objects that get discarded and add to the work the garbage collector has to do. Is this bad? Only in some cases, such as developing XNA games. If this is your case, you'll want to avoid uncontrolled boxing; if so, I would also invite you to stop by my blog where I have some bits of advice on that topic.

Vojislav Stojkovic
+1  A: 

"If I intend to pass that same value around as an Object do I really wanna unbox/box every time?"

The short answer: No, you wouldn't want to do a lot of boxing/unboxing. It creates overhead: extra extra garbage and tends to be slow (although I think the speed has been optimized in later framework versions).

EDIT: However, if you "pass that same value around as an Object", without casting is back to the value type until it's needed, then it stays boxed the whole way without being unboxed.

But, as everyone said, you don't need to "pass that same value around as an Object" anyway. That's what generics are for, unless you are working on Framework 1.x. Boxing was more relevant back then when the BCL collection classes used System.Object and any value type that went in was boxed.

(As an aside, boxed value types are NOT unboxed if accessed through an interface.)

Lucas