The below code fails at the last assignment:
static void Main(string[] args)
{
int a = 5;
object b = 5;
System.Diagnostics.Debug.Assert( a is int && b is int );
double x = (double)a;
double y = (double)b;
}
If both a and b are int, what is the cause of this error?
This is an extremely frequently asked question. See https://ericlippert.com/2009/03/03/representation-and-identity/ for an explanation.
Snippet:
I get a fair number of questions about the C# cast operator. The most frequent question I get is:
short sss = 123;
object ooo = sss; // Box the short.
int iii = (int) sss; // Perfectly legal.
int jjj = (int) (short) ooo; // Perfectly legal
int kkk = (int) ooo; // Invalid cast exception?! Why?
Why? Because a boxed T can only be unboxed to T. (*) Once it is unboxed, it’s just a value that can be cast as usual, so the double cast works just fine.
(*) Or Nullable<T>.
Unboxing requires the exact type - you can do this instead:
double y = (double)(int)b;
This is one of the rare cases where System.Convert comes in handy. You can use System.Convet.ToDouble(obj) to knock it out if you don't know before hand that it will be int.
Implicit casting is a compile-time operation. It's not possible for b of type object.
a is an int, but b is a reference to an object that is an int - it is what is called a boxed int. They are two different things, hence the different behaviors.
Related
I can cast byte to int without any problems.
byte a = 2;
int b = a; // => unboxing, boxing or conversion?
When I cast byte first to object and then to int I get an InvalidCastException.
byte a = 2;
object b = a; // => boxing?
int c = (int) b; // => unboxing fails?
But I can workaround this problem by using Convert.ToInt32.
byte a = 2;
object b = a; // => boxing?
int c = Convert.ToInt32(b); // => what happens here?
Why do I get an InvalidCastException in the second example?
What does Convert.ToInt32 in the background?
Did I label boxing, unboxing and conversion correctly? / What is the correct term when in the examples where I'm not sure?
Are the conversion operators at play here? Is there an overview about the basic conversion operators of the basic types?
Please don't hesistate to hint me other things I might have gotten wrong or missed.
Why do I get an InvalidCastException in the second example?
Because you specified you want to cast (and at the same time unbox) the type of the (boxed) variable to something else. And there is no built-in, implicit or explicit conversion operator defined, so it fails.
What does Convert.ToInt32 in the background?
This. It uses the IConvertible interface to do the conversion.
Did I label boxing, unboxing and conversion correctly? / What is the correct term when in the examples where I'm not sure?
int b = a; // => conversion
object b = a; // => boxing
int c = (int) b; // => casting fails
int c = Convert.ToInt32(b); // => what happens here: a method call that happens to do a conversion
Are the conversion operators at play here? Is there an overview about the basic conversion operators of the basic types?
Yes, although defined in the CLR.
Why do I get an InvalidCastException in the second example?
You can unbox in origin type only
What does Convert.ToInt32 in the background?
It contains a convertation
Did I label boxing, unboxing and conversion correctly? / What is the correct term when in the examples where I'm not sure?
byte a = 2;
int b = a; // convertation (byte to int)
object b = a; // boxing
int c = (int) b; //unboxing
object b = a; // boxing
int c = Convert.ToInt32(b); // convertation (object to int)
Are the conversion operators at play here? Is there an overview about the basic conversion operators of the basic types?
You can reflect framework code to learn how it works deeper.
should be
object b = a; // => boxing
int c = (int) b; //Un-boxing
I am reading a book to pass the Microsoft Exam 70-483 and I got a little stuck on the following question :
The following code is boxed into object o.
double d = 11.5;
object o = d;
You’re asked to cast object o into int. What option should you use in your code?
int i = (int)o; //Option : A
int i = (int)(double)o; //Option : B
int i = (int)(float)(double)o; //Option : C
int i = (float)o; //Option : D
According to book, true answer is C, but I could not understand why the option b is wrong. When I try to run both of them, results are the same. Is there any difference option B and option C? What are your opinions?
In your given example, option B is totally fine.
Option A: You can't box and unbox different types. This will result in an InvalidCastException.
Option B: Totally fine. o will be unboxed in a double. Afterwards it will be cast (explicit) in an integer.
Option C: Like B, but before casting it into an integer, it will be casted to an float. The Result will stay the same. There is absolutly no need for the cast from double to float. It will always truncate.
But there are edges cases! (See below)
Option D: Can't use implicit cast from float to int. Same as A: InvalidCastException
So why C?
There are cases where rounding a double to an int differs from rounding a float to an int.
See the following answer from supercat
You can reproduce this by the following example:
static void Main(string[] args)
{
double val = 12344321.4999999991;
int a = (int)(float)val;
int b = (int)val;
Console.WriteLine(a);
Console.WriteLine(b);
Console.ReadLine();
}
The output will be:
12344322
12344321
So as you can see, casting to float first, will result in a different outcome.
Keep in mind, that these are edge cases and most likely academic problems.
I'm preparing for an exam and stumbled over a question regarding boxing/unboxing.
I always thought if i box let's say a float like this:
float x = 4.5f;
object o = x;
If a want a value type variable back from o, I will have to unbox it to a float.
float y = (float)o;
This should throw an exception:
int z = int(o);
If I want to cast the value stored in o to an int I will have to unbox it first and cast afterwards like this:
int z = (int)(float)o;
Now the question i stumbled upon:
Suppose you have a method like this:
public static void FloorTemperature(float degrees) {
object degreesRef = degrees;
`xxx`
Console.WriteLine(result);
}
You need to ensure the application does not throw exceptions on invalid conversions. Which code segment should you insert for xxx (I think invalid conversion are invalid cast exceptions):
(a) int result = (int)degreesRef;
(b) int result = (int)(float)degreesRef;
The correct solution is (a), but to me (b) looks correct. So can you please enlighten me? What am I missing?
Kind regards
You aren't missing anything
The answer should be (b) because:
(a) throws an exception since you are trying to cast object to int.
(b) is correct since you first cast it to float then you cast it to int which rounds it but doesn't throw an exception.
I have an enum, Foo:
public enum Foo { Alpha, Bravo, Charlie }
If I attempt the following cast from a boxed int to a Foo?, I get an InvalidCastException:
var x = (Foo?)(object)1;
This led me to some experimentation...
var x = (Foo)(object)1; // succeeds
var x = (long)(object)1; // fails
var x = (long?)(object)1; // fails
var x = (long)1; // succeeds
var x = (long?)1; // succeeds
var x = (int)(object)1; // succeeds
var x = (int?)(object)1; // succeeds
What this tells me is that you can cast from a boxed int to an enum but not to a long, and you cannot convert from a boxed int to any kind of nullable except an int?.
By the way, the reason I'm casting the int to object first is that I'm really trying to cast from an int to a generic parameter TValue, like this:
var x = (TValue)(object)1;
If I didn't have (object), it wouldn't compile. (See this blog post by Eric Lippert for details.)
Questions
Why can you convert from a boxed int to an enum, but not to a nullable enum (and not to a long nor a long?)?
What's the easiest way to rewrite var x = (TValue)(object)1; so that it compiles, works at runtime, and is performant (assuming TValue is determined to be a Foo? at runtime)?
To answer the first question, you can convert from a boxed value to an enum only if the boxed value is of the enum's underlying type. Had you declared
enum Foo : byte { ...
you would not be able to cast from boxed int to Foo.
To answer the second question, try
var x = (TValue)Enum.ToObject(typeof(TValue), 1);
This involves boxing, though; if you need a solution that won't box, it will be more complicated.
Nullables are not some special category of atomic types, but shorthand for type Nullable<T>, which is why you can't cast a boxed int to a nullable enum. If you want to create a nullable enum, you can do it thusly:
var x = new Nullable<Foo>((Foo)1);
This answers both your questions, I think!
C# accepts the following:
object o = "hello";
int? i = o as int?;
if (i == null) {
// o was not a boxed int
}
else {
// Can use i.Value to recover the original boxed value
}
But not
String o = "hello";
int? i = o as int?;
if (i == null) {
// o was not a boxed int
}
else {
// Can use i.Value to recover the original boxed value
}
I'm just wondering about the behaviour of the keyword as in C#.
The same as in Java this would fail:
Object test1 = "hello";
Integer test2 = (Integer) test1;
String test3 = "hello";
Integer test4 = (Integer) test3; //compilation error
The compiler knows that a string can never be an int? so it tells you that. That doesn't mean that int? isn't useful. Your attempted use case is far from the normal one. The normal one is "I want to represent an integer and the possibility that the value is missing/unknown". For that, int? works extremely well.
Why would you expect your original code to work? Why would it be helpful?
Note that you can use as with nullable types, for unboxing:
object o = "hello";
int? i = o as int?;
if (i == null)
{
// o was not a boxed int
}
else
{
// Can use i.Value to recover the original boxed value
}
EDIT: Having seen your comment, you don't use as to parse things. You probably want to use int.TryParse:
string text = "123":
int value;
if (int.TryParse(text, out value))
{
Console.WriteLine("Parsed successfully: {0}", value);
}
else
{
Console.WriteLine("Unable to parse text as an integer");
}
If you're sure the string is meant to be an integer (i.e. it's a bug otherwise) then you can just use int.Parse:
int value = int.Parse(text);
That will throw an exception if the parsing fails.
Note also that both of these methods allows you to specify a format provider (usually a culture info) which allows you to express how numbers are expressed in that format (e.g. thousands separators).
EDIT: In answer to your new question, the compiler prevents this because it knows a string can't possibly be a boxed int - the conversion will never ever succeed. When it only knows that the original value is an object, it might succeed.
For instance, suppose I said to you, "Here's a shape: is it a square?" That's a sensible question. It's reasonable to ask it: you can't tell without looking at the shape.
If, however, I said: "Here's a triangle: is it a square?" Then you'd be reasonably entitled to laugh in my face, as a triangle can't possibly be a square - the question doesn't make sense.
int? means a nullable integer type, not an int that could contain any other type of variable.
If you want a variable type that could contain an int or a string, you'd have to use an object, or a string I suppose, and then live a life filled with type casting. I don't know why you would want to do that, though.
int? allows you to store any integer value, or a null value. Which is useful when say the answer to the question "How many orders has this person placed" is legitimately "I don't know" instead of a number of orders, or zero which would be "I know for a fact this person has never placed an order".
I want to add some further information.
An other case, why the cast is invalid and the compiler throws an error on compilation is, that System.String is marked as sealed. So the compiler knows from which types System.String inherites and to which types you can cast the string using the as-operator.
Due to the keyword sealed, the compiler also knows that you cannot inherit from System.String to add functionality or implement some additional interfaces.
The code below is an example and the compiler will throw the following error on compilation
Cannot convert type 'SealedClass' to
'ICastToInterface' via a reference
conversion, boxing conversion,
unboxing conversion, wrapping
conversion, or null type conversion
public class UnsealedClass {
// some code
}
public sealed class SealedClass {
// some code
}
public interface ICastToInterface {
// some code
}
public class Test {
public Test() {
UnsealedClass unsealedClass = new UnsealedClass();
SealedClass sealedClass = new SealedClass();
ICastToInterface unsealedCast = unsealedClass as ICastToInterface; // This works fine
ICastToInterface sealedCast = sealedClass as ICastToInterface; // This won´t compile, cause SealedClass is sealed
}
}
but you can check the value of null and set it to null.
int? blah;
if (blah == null)
{}
int? is a nullable integer, it has nothing to do with casting and the as keyword. "String" is a string type object, which is not convertible to an int (nullable or non-nullable).
The as Keyword is virtually the same as casting using brackets except it will not return an error, it will set the object to null:
int i = 1;
object o = i; //boxing
int j = (int)o; //unboxing
This first example works as the object assigned to o is an int.
Now consider:
string i = "MyString";
object o = MyString;
int j = (int)o //unboxing raises exception
int j = o as int; //raises compilation Error as int is not nullable
int? j = o as int?; /// o == null
I hope that that helps to explain the difference between the two concepts.
Richard