Casting, unboxing, conversion..? - c#

Recently I am learning value types and I am bit confused. Also both casting and unboxing uses the same syntax - (expected type)(object), right?
And what about simple conversion between types, that is casting or just conversion?
int x = (int)2.5; //casting?
object a=x;
int Y=(int)a; //unboxing I think
Random r=new Random();
object X=r;
Random R=(Random)X; // casting

There's a lot of things to consider here, but let's tackle the simplest first:
What is the syntax (type)expression?
Well, in its basic form, it is considered casting. You cast the expression from one type, to another. That's it.
However, what exactly happens, that depends on the type, and a lot of other things.
If casting a value type to something else, you depend on one of the two types involved to declare a casting operator that handles this. In other words, either the value type need to define a casting operator that can cast to that other type, or that other type need to define a casting operator that can cast from the original type.
What that operator does, is up to the author of that operator. It's a method, so it can do anything.
Casting a value type to some other type gives you a different value, a separate value type, or a new reference type, containing the new data after casting.
Example:
int a = (int)byteValue;
Boxing and unboxing comes into play when you're casting a value type to and from a reference type, typically object, or one of the interfaces the value type implements.
Example:
object o = intValue; // boxing
int i = (int)o; // unboxing
Boxing also comes into play when casting to an interface. Let's assume that "someValueType" is a struct, which also implements IDisposable:
IDisposable disp = (IDisposable)someValueType; // boxed
Casting a reference type, can do something else as well.
First, you can define the same casting operators that were involved in value types, which means casting one reference type to another can return a wholly new object, containing quite different type of information.
Boxing does not come into play when casting a reference type, unless you cast a reference type back to a value type (see above.)
Example:
string s = (string)myObjectThatCanBeConvertedToAString;
Or, you can just reinterpret the reference, so that you still refer to the same object in question, but you are looking at it through a different pair of type glasses.
Example:
IDisposable disp = (IDisposable)someDisposableObject;

One important restriction on unboxing is that you can only unbox to the exact value-type(or its nullable equivalent) and not to another value-type the original value-type is convertible to.
int myInt = 1;
object x = myInt; // box
int unbox1 = (int)x; // successful unbox
int? unbox2 = (int?)x; // successful unbox
long unbox3 = (long)x; // error. Can't unbox int to long
long unbox4 = (long)(int)x; // works. First it unboxes to int, and then converts to long
Another interesting bit is that a nullable value-type gets boxed as its non nullable type, and can be unboxed as both the nullable and the non nullable type. Since a nullable that has the value null boxes to the reference null the information about its type is lost during the boxing.

A cast is one form of conversion, basically.
All the comments in your code are correct.
It's important to note the difference between a reference conversion and other conversions, however. The final conversion you've shown is a reference conversion - it maintains representational identity, so the values of X and R are both references to the same object.
Compare this with a double to int conversion (changes form significantly) and unboxing (copies the value from inside the box to the variable).
The difference is important for some topics such as generic variance - that only works with reference types because of the reference conversions available. Basically the CLR checks that it all the types are appropriate, and then runs the appropriate code without ever having to perform any actual conversions on the references themselves.

Boxing and Unboxing specifically refer to casting value types to reference types and back again. Check out this link for more Boxing and Unboxing (C# Programming Guide).

Boxing and unboxing is done behind the scenes by compiler. So correct comments would be
int x = (int)2.5; //casting with conversion
object a=x; //casting with boxing
int Y=(int)a; //casting with unboxing
Random r=new Random();
object X=r;
Random R=(Random)X; //casting without unboxing
about casting vs conversion check this question: What is the difference between casting and conversion?

Related

Why explicit conversion from object to long (Unboxing) is not allowed in C#? [duplicate]

We can cast int to long.
Why is the following piece of code gives a run time error.
object o = 9;
long i = (long)o;
Console.WriteLine(i);
I am new to C#.
The existing answers rightly explain why you get an exception, but don't really show what you could do instead. First casting to int is the typical answer, but only works if you know that the original value was an int. If the original value could be either int or long, then unboxing as int could fail just as well.
The simple way to change your code to something that works is by not attempting to do it yourself. .NET Framework already has a method to do exactly what you want: Convert.ToInt64. So just write
long i = Convert.ToInt64(o);
and let the runtime worry about any internally required intermediate type conversions.
Per this bit of documentation:
For the unboxing of value types to succeed at run time, the item being unboxed must be a reference to an object that was previously created by boxing an instance of that value type. Attempting to unbox null or a reference to an incompatible value type will result in an InvalidCastException.
So, you have to unbox to int first and then convert to long (an implicit conversion exists):
object o = 9;
long i = (int)o;
Since both int and long are value types, they are copied by value, and not by reference like reference types (e.g. object). So o is of a reference type, so the int object is stored by reference. In order to put it in a new variable of a value type, you need to have some value which you can actually copy into that.
So you need to cast the object to its actual type (int) first before you can perform a type conversion to long:
object o = 9;
long i = (long)(int)o;
You don’t necessarily need to do the (long) though as that happens implicitely when storing the value in the long.

Why can't you unbox directly to a type which you can explicity cast to? [duplicate]

This question already has an answer here:
Specified cast is not valid #3
(1 answer)
Closed 3 years ago.
Ran into this as part of some EF/DB code today and ashamed to say I'd never encountered it before.
In .NET you can explicitly cast between types. e.g.
int x = 5;
long y = (long)x;
And you can box to object and unbox back to that original type
int x = 5;
object y = x;
int z = (int)y;
But you can't unbox directly to a type that you can explicitly cast to
int x = 5;
object y = x;
long z = (long)y;
This is actually documented behaviour, although I never actually run into it until today. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing#example-1
For the unboxing of value types to succeed at run time, the item being unboxed must be a reference to an object that was previously created by boxing an instance of that value type. Attempting to unbox null causes a NullReferenceException. Attempting to unbox a reference to an incompatible value type causes an InvalidCastException.
I'm curious is there some technical reason why this isn't possible/supported by the runtime?
The cast syntax (Foo)bar C# does one of these things:
cast a reference type to another reference type
convert a value type to another value type
box a value type
unbox a boxed value type
These operations are semantically very different. It really makes more sense to think of them as four distinct operations which by historical accident happen to share the same (Foo)bar syntax. In particular they have different constraints on what information need to be known at compile time:
an unboxing operation need to know the type of the unboxed value
a value type conversion need to know both the source and target types.
The is basically because the compiler needs to know at compile time how many bytes to allocate to the values. In your example, the information that the boxed value is an int is not available at compile time, which means neither the unboxing nor the conversion to a long can be compiled.
What is counter-intuitive here is that the same constraints does not apply to reference types. Indeed the whole point of casting reference types is that the compiler don't know the exact type at compile time. You use a cast when you know better then the compiler, and the compiler accepts that, and then at runtime performs a type check to ensure that cast is valid.
This is possible due to to some fundamental differences in reference types:
A reference type instance knows its own exact type at runtime. It is stored as part of the instance data.
Reference types are polymorphic, which means the compiler does not need to know the exact instance type. All references have the same size, so there is no ambiguity about how many bytes to allocate.
These semantic differences between the different kinds of casts means they cannot be merged without compromising safety.
Lets say C# supported unbox-and-convert in a single cast expression:
int x = 70000;
object y = x;
short z = (short)y;
Currently an unboxing cast indicates that you expect that the boxed value is of the given type. If this is not the case, an exception is thrown, so you discover the bug. But a value-type conversion using cast syntax indicates that you know the types are different and that the conversion may lead to data loss.
If the language would automatically unbox and convert then there would be no way to express if you wanted a safe unboxing without any risk of data loss.
I don't know that I can summarise all of Eric Lippert's article on this issue succinctly, but a section of the article that I find relevant to your question specificially is the following;
"[there are] ... certain conversions that the C# compiler thinks of as
representation-changing are actually seen by the CLR verifier as
representation-preserving. For example, the conversion from int to
uint is seen by the CLR as representation-preserving because the 32
bits of a signed integer can be reinterpreted as an unsigned integer
without changing the bits. These cases can be subtle and complex, and
often have an impact on covariance-related issues.
I’m also ignoring conversions involving generic type parameters which
are not known at compile time to be reference or value types. There
are special rules for classifying those which would be major
digressions to get into.
Anyway, we can think of representation-preserving conversions on
reference types as those conversions which preserve the identity of
the object. When you cast a B to a D, you’re not doing anything to the
existing object; you’re merely verifying that it is actually the type
you say it is, and moving on. The identity of the object and the bits
which represent the reference stay the same. But when you cast an int
to a double, the resulting bits are very different."

C# - Issues with boxing / unboxing / typecasting ints. I don't understand

I'm having a hard time understanding this. Consider the following example:
protected void Page_Load(object sender, EventArgs e)
{
// No surprise that this works
Int16 firstTest = Convert.ToInt16(0);
int firstTest2 = (int)firstTest;
// This also works
object secondTest = 0;
int secondTest2 = (int)secondTest;
// But this fails!
object thirdTest = Convert.ToInt16(0);
int thirdtest2 = (int)thirdTest; // It blows up on this line.
}
The specific error that I get at runtime is Specified cast is not valid. If I QuickWatch (int)thirdTest in Visual Studio, I get a value of Cannot unbox 'thirdTest' as a 'int'.
What the heck is going on here?
Unboxing checks the exact type as explained in the documentation.
Unboxing is an explicit conversion from the type object to a value
type or from an interface type to a value type that implements the
interface. An unboxing operation consists of:
Checking the object instance to make sure that it is a boxed value of
the given value type.
Copying the value from the instance into the value-type variable.
As you can see the first step is to check that the object instance matches the target type.
Also quote from the documentation:
For the unboxing of value types to succeed at run time, the item being
unboxed must be a reference to an object that was previously created
by boxing an instance of that value type. Attempting to unbox null
causes a NullReferenceException. Attempting to unbox a reference to an
incompatible value type causes an InvalidCastException.
So to fix this error make sure that the type matches before attempting to unbox:
object thirdTest = Convert.ToInt16(0);
short thirdtest2 = (short)thirdTest;
What's going on is exactly what it says.
In the first case, you have a short, unboxed, that you are then explicitly typecasting to an int. This is a valid conversion that the compiler knows how to do, so it works.
In the second case, you have an int, boxed, that are are assigning back to an int. This is a simple unboxing of an integer, which also valid, so it works.
In the third case, you have a short, boxed, that are you trying to unbox into a variable that is not a short. This isn't a valid operation: you can't do this in one step. This is not an uncommon problem, either: if you are using, for example, a SqlDataReader that contains a SMALLINT column, you cannot do:
int x = (int)rdr["SmallIntColumn"];
Either of the following should work in your third example:
object thirdTest = Convert.ToInt16(0);
int thirdTest2 = Convert.ToInt32(thirdTest);
int thirdTest3 = (int)(short)thirdTest;
Int16 is a fancy way to write short; there is no boxing/unboxing going on there, just the plain CLR conversion between 16-bit and 32-bit integers.
The second case boxes and unboxes to the same type, which is allowed: value type int gets wrapped in an object, and then gets unwrapped.
The third case tries to unbox to a different type (int instead of short) which is not allowed.

boxing and unboxing in int and string

I am little bit confused in boxing and unboxing. According to its definition
Boxing is implicit conversion of ValueTypes to Reference Types (Object).
UnBoxing is explicit conversion of Reference Types (Object) to its equivalent ValueTypes.
the best example for describing this is
int i = 123; object o = i; // boxing
and
o = 123; i = (int)o; // unboxing
But my question is that whether int is value type and string is reference type so
int i = 123; string s = i.ToString();
and
s = "123"; i = (int)s;
Is this an example of boxing and unboxing or not???
Calling ToString is not boxing. It creates a new string that just happens to contain the textual representation of your int.
When calling (object)1 this creates a new instance on the heap that contains an int. But it's still an int. (You can verify that with o.GetType())
String can't be converted with a cast to int. So your code will not compile.
If you first cast your string to object your code will compile but fail at runtime, since your object is no boxed int. You can only unbox an value type into the exactly correct type(or the associated nullable).
Two examples:
Broken:
object o=i.ToString();// o is a string
int i2=(int)o;//Exception, since o is no int
Working:
object o=i;// o is a boxed int
int i2=(int)o;//works
int i = 2;
string s = i.ToString();
This is NOT boxing. This is simply a method call to Int32.ToString() which returns a formatted string representing the value of the int.
i = (int)s;
This code will not compile as there is no explicit conversion defined between System.String and System.Int32.
Think of it in the following way to understand what is and what is not boxing and unboxing:
Boxing: Its when you take a value type and just "stick" it in a reference variable. There is no need of any type specific conversion logic for this operation to work. The variable type will still be the same if you use GetType().
Unboxing: Its just the opposite operation. Take a value type stuck in a reference object and assign it to a value type variable. Again there is no need for any type specific conversion logic for this operation to work.
So if (int)s were valid, it would simply be a explicit conversion and not a unboxing operation, becuase s.GetType() would return System.String, not System.Int32.
Boxing/Unboxing: Conversion of Value Types to its object representation and vice versa (e.g. int and object).
The ToString() method in contrast is an operation which generated a new string, is has nothing to do with boxing/cast/type conversation.
Late to the party on this, but...... I don't like simply reading answers and without proofs behind them. I like to understand the problem and analyse the possible solution and see if it ties in with my understanding. This copy and paste text from the rightly acclaimed excellent 'CLR via C#' by the god Jeff Richter explains this:
Even though unboxed value types don’t have a type object pointer, you can still call virtual methods (such as Equals, GetHashCode, or ToString) inherited or overridden by the type. If your value type overrides one of these virtual methods, then the CLR can invoke the method nonvirtually because value types are implicitly sealed and cannot have any types derived from them. In addition, the value type instance being used to invoke the virtual method is not boxed. However, if your override of the virtual method calls into the base type's implementation of the method, then the value type instance does get boxed when calling the base type's implementation so that a reference to a heap object get passed to the this pointer into the base method. However, calling a nonvirtual inherited method (such as GetType or MemberwiseClone) always requires the value type to be boxed because these methods are defined by System.Object, so the methods expect the this argument to be a pointer that refers to an object on the heap.
Mr Richter should be given a medal for this book. If you haven't got it, get it!! Then you'll get it :)

Boxing / Unboxing Nullable Types - Why this implementation?

Extract from CLR via C# on Boxing / Unboxing value types ...
On Boxing: If the nullable instance is not null, the CLR takes the value out of the nullable instance and boxes it. In other words a Nullable < Int32 > with a value of 5 is boxed into a boxed-Int32 with a value of 5.
On Unboxing: Unboxing is simply the act of obtaining a reference to the unboxed portion of a boxed object. The problem is that a boxed value type cannot be simply unboxed into a nullable version of that value type because the boxed value doesn't have the boolean hasValue field in it. So, when unboxing a value type into a nullable version, the CLR must allocate a Nullable < T > object, initialize the hasValue field to true, and set the value field to the same value that is in the boxed value type. This impacts your application performance (memory allocation during unboxing).
Why did the CLR team go through so much trouble for Nullable types ? Why was it not simply boxed into a Nullable < Int32 > in the first place ?
I remember this behavior was kind of last minute change. In early betas of .NET 2.0, Nullable<T> was a "normal" value type. Boxing a null valued int? turned it into a boxed int? with a boolean flag. I think the reason they decided to choose the current approach is consistency. Say:
int? test = null;
object obj = test;
if (test != null)
Console.WriteLine("test is not null");
if (obj != null)
Console.WriteLine("obj is not null");
In the former approach (box null -> boxed Nullable<T>), you wouldn't get "test is not null" but you'd get "object is not null" which is weird.
Additionally, if they had boxed a nullable value to a boxed-Nullable<T>:
int? val = 42;
object obj = val;
if (obj != null) {
// Our object is not null, so intuitively it's an `int` value:
int x = (int)obj; // ...but this would have failed.
}
Beside that, I believe the current behavior makes perfect sense for scenarios like nullable database values (think SQL-CLR...)
Clarification:
The whole point of providing nullable types is to make it easy to deal with variables that have no meaningful value. They didn't want to provide two distinct, unrelated types. An int? should behaved more or less like a simple int. That's why C# provides lifted operators.
So, when unboxing a value type into a nullable version, the CLR must allocate a Nullable<T> object, initialize the hasValue field to true, and set the value field to the same value that is in the boxed value type. This impacts your application performance (memory allocation during unboxing).
This is not true. The CLR would have to allocates memory on stack to hold the variable whether or not it's nullable. There's not a performance issue to allocate space for an extra boolean variable.
I think it makes sense to box a null value to a null reference. Having a boxed value saying "I know I would be an Int32 if I had a value, but I don't" seems unintuitive to me. Better to go from the value type version of "not a value" (a value with HasValue as false) to the reference type version of "not a value" (a null reference).
I believe this change was made on the feedback of the community, btw.
This also allows an interesting use of as even for value types:
object mightBeADouble = GetMyValue();
double? unboxed = mightBeADouble as double?;
if (unboxed != null)
{
...
}
This is more consistent with the way "uncertain conversions" are handled with reference types, than the previous:
object mightBeADouble = GetMyValue();
if (mightBeADouble is double)
{
double unboxed = (double) mightBeADouble;
...
}
(It may also perform better, as there's only a single execution time type check.)
A thing that you gain via this behavior is that the boxed version implements all interfaces supported by the underlying type. (The goal is to make Nullable<int> appear the same as int for all practical purposes.) Boxing to a boxed-Nullable<int> instead of a boxed-int would prevent this behavior.
From the MSDN Page,
double? d = 44.4;
object iBoxed = d;
// Access IConvertible interface implemented by double.
IConvertible ic = (IConvertible)iBoxed;
int i = ic.ToInt32(null);
string str = ic.ToString();
Also getting the int from a boxed version of a Nullable<int> is straightforward - Usually you can't unbox to a type other than the original src type.
float f = 1.5f;
object boxed_float = f;
int int_value = (int) boxed_float; // will blow up. Cannot unbox a float to an int, you *must* unbox to a float first.
float? nullableFloat = 1.4f;
boxed_float = nullableFloat;
float fValue = (float) boxed_float; // can unbox a float? to a float Console.WriteLine(fValue);
Here you do not have to know if the original version was an int or a Nullable version of it. (+ you get some perf too ; save space of storing the the hasValue boolean as well in the boxed object)
I guess that is basically what it does. The description given includes your suggestion (ie boxing into a Nullable<T>).
The extra is that it sets the hasValue field after boxing.
I would posit that the reason for the behavior stems from the behavior of Object.Equals, most notably the fact that if the first object is null and the second object is not, Object.Equals returns false rather than call the Equals method on the second object.
If Object.Equals would have called the Equals method on the second object in the case where the first object was null but the second was not, then an object which was null-valued Nullable<T> could have returned True when compared to null. Personally, I think the proper remedy would have been to make the HasValue property of a Nullable<T> have nothing to do with the concept of a null reference. With regard to the overhead involved with storing a boolean flag on the heap, one could have provided that for every type Nullable<T> there would a be a static boxed empty version, and then provide that unboxing the static boxed empty copy would yield an empty Nullable<T>, and unboxing any other instance would yield a populated one.

Categories

Resources