views:

2071

answers:

5

Hi,

I'm going to try to make myself clearer. Why does C# accept 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
}

and not

String = "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 NOT trying to find a way to convert a string into an int. I already know how to reach it. I'm just wondering about the behaviour of the keyword as in C#

String inherits from System.Object, so why does the first conversion work but not the second one?

+2  A: 

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".

kscott
A: 

but you can check the value of null and set it to null.

int? blah;
if (blah == null)
{}
CSharpAtl
+8  A: 

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.

Jon Skeet
Right, but why does this not work?<code> 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 }</code>
Juan Carlos Blanco Martínez
because you are trying to assign a string value to an int. the question mark just allows int to contain a null value, it does not change the fact that it is an int.
kscott
Juan: You don't use a cast or "as" to convert between strings and other types. There isn't an implicit or explicit conversion available - you have to use a parsing method. See the second half of my answer for details and an example.
Jon Skeet
I completely agree, as I said I'm not looking to a way to convert from String to int, I'm just trying to understand why it behaves so. Don't you find anything weird in my question?Thanks
Juan Carlos Blanco Martínez
Nope - I would never have expected a conversion operator to try to do parsing. Oops - it looks like I misread your edited question, I thought it said you *were* trying to find a way to do the conversion. I'll leave the extra info there for future readers though.
Jon Skeet
But anyways, have you got any explanation to my new question?
Juan Carlos Blanco Martínez
Have edited answer - see the bit at the bottom.
Jon Skeet
Thanks a lot. It's a pleasure to read your answers.
Juan Carlos Blanco Martínez
It's a pleasure to write them too :)
Jon Skeet
Just one think. Do you think that the behaviour commented above (String -> Int (compilation error), Object -> Int (Right) is proper for a Object-oriented language, since String inherits from Object?Just wondering if you would implement this in a different way or you completely agree.Thanks
Juan Carlos Blanco Martínez
No, I think it's fine - it's an explicit conversion, so you're telling the compiler "I think this will be okay at execution time". It makes perfect sense to me that you can't tell the compiler you think something will work when the compiler knows for certain that it won't.
Jon Skeet
A: 

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

Richbits
"int j = o as int" doesn't raise an exception - it prevents compilation, because there's no value for j to take in the case where o isn't a boxed int.
Jon Skeet
Sorry, thanks for the correction Jon... I've updated the answer correspondingly... (hopefully correctly)
Richbits
A: 

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
  }
}
Jehof