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!
Related
I have read in CLR via C#:
Unboxing is really just the operation of obtaining a pointer to the
raw value type (data fields) contained within an object.
which means that if more than one value type is contained within an object they can be unboxed using the below syntax:
int a= (int) o; //assigns a to 10
char b= (char) o; // assigns b to 'b'
How to implement such object which supports multiple unboxing?
A boxed value can only be the boxed form of a single type - if you call o.GetType() you'll find out what that is.
In general, you can only unbox to the exact same type, with a few wrinkles:
You can unbox an enum value to its underlying integral type, and vice versa
There's no such thing as a boxed nullable value type - boxing will result in either a boxed form of the non-nullable type, or a null reference. You can unbox to a nullable value type, and the result will either be a null value (if the original reference was a null reference) or a non-null value containing the unboxed non-nullable value, if you see what I mean.
So for example:
object o = 10;
FileMode mode = (FileMode) o; // Valid conversion to enum
int? n = (int?) o; // n has a value of 10
n = null;
o = n; // o's value is a null reference
n = (int?) o; // n's value is the null int? value
Are you talking about casting? In .NET if you have a boxed type it has a specific type and you can only cast it to the actual value type to unbox:
object o=10; // o is a boxed int with value 10
var a=(int)o; // works
//var b=(byte)o; // ERROR, this is not what's contained in it
var b=(byte)(int)o; // works, you get the int out then cast it to byte
var b=Convert.ToByte(o); // also works, using IConvertible
However, the main purpose of generics (initially at least) was to avoid boxing value types at all because of the performance costs involved. You can change most instances of object to generic values and preserve the types, and when you use them you'll have the proper value type right away.
If you have a custom object with for example both an int or a string, you could do this with explicit conversion operators.
Example;
public class CustomObject
{
int Number;
string Text;
public CustomObject(int number, string text) //constructor
{
Number = number;
Text = text;
}
public static explicit operator int(CustomObject o)
{
return o.Number;
}
public static explicit operator string(CustomObject o)
{
return o.Text;
}
}
Fiddle example
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.
This question already has an answer here:
Closed 12 years ago.
Possible Duplicate:
Why does this conversion doesn't work?
Hi,
i discovered a strange behaviour of the framework.
This code throws an exception:
byte a = 1;
object b = a;
Console.WriteLine(b.GetType());
Console.WriteLine((byte)b);
Console.WriteLine((int)(byte)b);
Console.WriteLine(Convert.ToInt32(b));
Console.WriteLine((int)b);
The last line throws a System.InvalidCastException.
I'd like to know what are the mechanism in the framework that make this code illegal.
Is it a problem of boxing/unboxing?!
Yes, boxed value types can only be unboxed to the exact same type.
The variable b is a boxed byte.
When you do (int)(byte)b you're unboxing b back to a byte and then converting that unboxed byte to an int.
When you do (int)b you're attempting to unbox b directly to an int, which is illegal.
Edit...
As Jon mentions in his answer, there are cases where you don't have to unbox to the exact same type. Specifically:
A boxed T can be unboxed to Nullable<T>.
A boxed Nullable<T> can be unboxed to T, assuming that the nullable isn't actually null.
A boxed enum with an underlying type of T can be unboxed to T.
A boxed T can be unboxed to an enum with an underlying type of T.
Eric Lippert has a blog post on this.
Why? Because a boxed T can only be
unboxed to T.
Or Nullable< T >.
When you unbox, it has to be to one of the following:
The exact same type
The nullable form of the exact same type
If it's an enum type value, then you can unbox to the underlying type
If it's an integral type value, you can unbox to an enum which uses that underlying type
Examples:
using System;
class Test
{
enum Foo : short
{
Bar = 1
}
static void Main()
{
short x = 1;
object o = x;
short a = (short) o;
short? b = (short?) o;
Foo c = (Foo) o;
o = Foo.Bar;
short d = (short) o;
}
}
Anything else will give an exception. In particular, you can't unbox to a different type even if there's an implicit conversion from the actual type to your target type, which is what you're trying to do on the last line of your example.
You also can't unbox from an integral value to a nullable form of an enum with the same underlying type (or the reverse situation).
Note that if you box a nullable value type value, the result is either null (if the original value was the null value for the type) or the boxed non-nullable value... there's no such thing as a "boxed nullable value type" if you see what I mean.
Is it a problem of boxing/unboxing?
Yes. bis a boxed byte. So you need to unbox it to a byte first , like in (int)(byte)b
This question already has answers here:
Closed 13 years ago.
Possible Duplicates:
Casting: (NewType) vs. Object as NewType
Why is the C# “as” operator so popular?
Hey,
I know this may be a silly question but this doubt came to me today.
What is the difference between doing
String text = (String) variable;
and
String text = variable as String;
?
A cast can do three things:
Perform a user-defined conversion
Perform an unboxing conversion
Perform a reference conversion
An as operation is almost always a reference conversion, the only exception being unboxing to a nullable type:
object x = "hello";
int? y = x as int?; // y is null afterwards
Then there's the behaviour with conversions which fail at execution time. So the differences are:
Casts performing reference conversions or unboxing will throw InvalidCastException on failure; as will result in the null value of the target type instead
Casts can perform user-defined conversions; as can't
Casts can unbox to non-nullable value types; as can only be used for unboxing if the target type is a nullable value type
as will return null if variable isn't actually of that type (String in this case). The cast will throw an exception.
Here is the link to Eric Lippert's blog on casting in C#. I'd summarize it, but it's pretty short and he'll explain it much better than me.
http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx
And here is his post on the cast operator:
http://blogs.msdn.com/ericlippert/archive/2009/03/19/representation-and-identity.aspx
There are a lot of different ways to cast in C#.
This will try to cast the reference to a String reference. If the cast fails, it throws an exception:
string text = (String) variable;
This will try to cast the reference to a String reference. If the cast fails, it will return a null reference to be assigned to the variable:
string text = varible as String;
This will cast a string reference to an object reference, which is a safe casting as String inherits from Object:
object text = (object)"1337";
Casting to a parent class can also be done implicitly:
object text = "1337";
This will box a value inside an object, then unbox it to a plain value again:
int value = 42;
object boxed = (object)value;
int valueAgain = (int)boxed;
The boxing can also be done implicitly:
int value = 42;
object boxed = value;
int valueAgain = (int)boxed;
This will make a widening conversion from byte to int:
byte a = 42;
int b = (int)a;
The same works as an implicit conversion:
byte a = 42;
int b = a;
This will make a narrowing conversion from int to byte, throwing away the overflow:
int a = 512;
byte b = (byte)a; // b now contains 0
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