unboxing : Please tell me the difference

  • Thread starter Thread starter chenedor
  • Start date Start date
C

chenedor

Hi all
I am a bit confuse about unboxing... what is the difference and what
is best practice ?

object o = __box(1234);

int n = *dynamic_cast<__box int*>(o);

of

int n = Convert::ToInt32(o);

thanks in advance
 
Hello !

The first approach is correct, in terms of language specification. The
Convert class is used to convert .NET basic types between each other. The
..NET basic types are Boolean, Char, SByte, Byte, Int16, Int32, Int64,
UInt16, UInt32, UInt64, Single, Double, Decimal, DateTime and String.

Boxing and unboxing are terms used to - as documentation states -
"conversion between managed objects and value types". In english, the most
common use is to convert from C++ base types 'int, char, float, double, long
etc' to .NET basic types and back again. The easiest way to think of this is
remembering that C++ basic types and .NET basic types are two different
worlds. To convert from C++ basic type into a .NET basic type, you use
boxing. To convert back, you use unboxing.

Also, boxing and unboxing are used when converting from __value classes to
managed objects and back again. Remember that unless otherwise specified,
Managed C++ creates the .NET basic types as value types. Thus, even these
types can be converted to objects and vice versa. However, when dealing with
..NET, you should always attempt to create it's types and objects into the
managed heap, and leave the unmanaged stack and heap for unmanaged types.
Mixing these two is a sure way to summon trouble your way.

So, "the correct way" to do this is:

object o = __box( (int)1234 );

int n = *dynamic_cast<__box int*>(o);

-Antti Keskinen
 
Antti said:
Hello !

The first approach is correct, in terms of language specification. The
Convert class is used to convert .NET basic types between each other. The
.NET basic types are Boolean, Char, SByte, Byte, Int16, Int32, Int64,
UInt16, UInt32, UInt64, Single, Double, Decimal, DateTime and String.

Boxing and unboxing are terms used to - as documentation states -
"conversion between managed objects and value types". In english, the most
common use is to convert from C++ base types 'int, char, float, double, long
etc' to .NET basic types and back again. The easiest way to think of this is
remembering that C++ basic types and .NET basic types are two different
worlds. To convert from C++ basic type into a .NET basic type, you use
boxing. To convert back, you use unboxing.

Also, boxing and unboxing are used when converting from __value classes to
managed objects and back again. Remember that unless otherwise specified,
Managed C++ creates the .NET basic types as value types. Thus, even these
types can be converted to objects and vice versa. However, when dealing with
.NET, you should always attempt to create it's types and objects into the
managed heap, and leave the unmanaged stack and heap for unmanaged types.
Mixing these two is a sure way to summon trouble your way.

So, "the correct way" to do this is:

object o = __box( (int)1234 );


Object *o, and the cast inside __box is not needed.


int n = *dynamic_cast<__box int*>(o);


Apart from being mentioned in an MS book, what is the reason of using
dynamic_cast here and not static_cast?



What I consider as the correct way:


#using <mscorlib.dll>

int main()
{
using namespace System;

Object *o = __box(1234);

int n = *static_cast<__box int*>(o);
}
 
Apart from being mentioned in an MS book, what is the reason of using
dynamic_cast here and not static_cast?

What I consider as the correct way:

int main()
{
using namespace System;

Object *o = __box(1234);

int n = *static_cast<__box int*>(o);
}

Hi,

It was my belief (though this is coming from a mainly native C++ background)
that static_cast was generally frowned upon (mainly because of the lack of
type-safety)... is this not considered the case in managed C++? In any case,
apart from the (certainly in this case) insignificant performance hit, what
would be the advantage one way or the other? Certainly in the past I've only
ever used static_cast in conversions between numeric types (e.g. ints to
floats) and although in this case you're pretty sure 'o' is a boxed int,
that might not necessarily be the case in a slightly more complex example.

Steve
 
Steve said:
Hi,

It was my belief (though this is coming from a mainly native C++ background)
that static_cast was generally frowned upon (mainly because of the lack of
type-safety)... is this not considered the case in managed C++? In any case,
apart from the (certainly in this case) insignificant performance hit, what
would be the advantage one way or the other? Certainly in the past I've only
ever used static_cast in conversions between numeric types (e.g. ints to
floats) and although in this case you're pretty sure 'o' is a boxed int,
that might not necessarily be the case in a slightly more complex example.


Check this for a quick overview of casts:

http://groups.google.com/groups?hl=...bbt7t%241b79%241%40ulysses.noc.ntua.gr&rnum=4


If the above does not work, try this:

http://groups.google.com/[email protected]&output=gplain



dynamic_cast is for RTTI (Run-time type identification) and its return
value should be checked before use. It is used in cases like:



void somefunc(void *p)
{

SomeClass *r= dynamic_cast<SomeClass *>(p);

// p is a SomeClass
if(r!=0)
// ...

// ...
}


and so on.



static_cast on the other hand are compile-time checked (that they are
related) conversions between types in the same hierarchy.


So static_cast makes sense here.
 
chenedor said:
Hi all
I am a bit confuse about unboxing... what is the difference and what
is best practice ?

object o = __box(1234);

int n = *dynamic_cast<__box int*>(o);

A couple of comments. First, the above may exhibit undefined behavior or
cause a NullReferenceException. If the dynamic_cast fails, it returns a
null pointer, which the code above de-references anyway. If you're going to
use dynamic_cast, it only makes sense if you write something like:

__box int* pi = dynamic_cast<__box int*>(o);

if (pi)
n = *pi;

Second, under the CLR all casts are dynamic as is the un-boxing operation.
Here's a simple example to illustrate the differences under the covers:

#using <mscorlib.dll>

#pragma managed

void f()
{
System::Object* o = __box(1234);
int n = *dynamic_cast<__box int*>(o);
int m = *static_cast<__box int*>(o);
}

Compile this with cl -clr -c -FAsc and look at the .COD file that's
produced:

; 6 : {

0000c 14 ldnull 0 ; i32 0x0
0000d 0a stloc.0 ; _o$

; 7 : System::Object* o = __box(1234);

0000e 20 d2 04 00 00 ldc.i4 1234 ; i32 0x4d2
00013 8c 00 00 00 00 box ?<clref>@Int32@System@@2HA
00018 0a stloc.0 ; _o$

; 8 :
; 9 : int n = *dynamic_cast<__box int*>(o);

00019 06 ldloc.0 ; _o$
0001a 75 00 00 00 00 isInst ?<clref>@Int32@System@@2HA
0001f 79 00 00 00 00 unbox ?<clref>@Int32@System@@2HA
00024 4a ldind.i4
00025 0c stloc.2 ; _n$

; 10 :
; 11 : int m = *static_cast<__box int*>(o);

00026 06 ldloc.0 ; _o$
00027 79 00 00 00 00 unbox ?<clref>@Int32@System@@2HA
0002c 4a ldind.i4
0002d 0b stloc.1 ; _m$
$L1344:

; 12 : }

Note that the unbox operation is identical in both cases, and that both
cases pass the expected type (System::Int32) to the unbox operation. The
only difference is that dynamic_cast inserted an isInst instruction, the
result of which was ignored.

So, there is reason to use dynamic_cast if you're not sure the cast will
succeed, but in those cases be sure to test the result of the cast before
using it, otherwise you're just turning a bad cast exception into a numm
reference exception. When you "know" that the cast will succeed, use
static_cast. The same rules apply in native C++ programming, except that
de-referencing a null pointer is undefined behavior instead of a guaranteed
NullReferenceException.

-cd
 
Back
Top