35

Beyond the regular boring difference between Cast and As

  • if i know that apple is a Fruit so I can use (Fruit)apple - and it throws an exception if it aint
  • as value can be checked against null to see if succeeded [won't throw Exception...]

However Ive been reading @EricLippert article about this and there was a nice sample about Nullable Value Types :

short? s = (short?)123;
int? i = s as int?;

this won't compile...

Cannot convert type 'short?' to 'int?' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion

Fine.

so why this :

    short? s = (short?)123;
    int? i = (int?)s;

Does Compile ? ( Against ALL Expectations ! I KNOW that s is not int? - and it should go BANG but it aint ...)

the Cast checking here should be much more deadly than the former example (which went Bang)

I feel bad asking about this much-talked subject.

Thanks in Advance.

Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • 1
    you described exactly what Eric Lippert wrote - the cast would compile while the `as` operator won't for cases regarding nullable types. – Yahia Apr 08 '12 at 18:55
  • @Yahia but are we agree that short is NOT INT ? – Royi Namir Apr 08 '12 at 18:59
  • short is not int BUT can be cast/converted to int - and that is what you are telling the compiler: make this short an int. – Yahia Apr 08 '12 at 19:01
  • @Yahia Cast checking should be much more deadly...thats his job...to throw exception.......? – Royi Namir Apr 08 '12 at 19:01
  • Why ? You are explicitley telling the compiler to use any way it knows to accomodate you... if it does not find a way it throws... – Yahia Apr 08 '12 at 19:02
  • And one more thing - `int? i2 = (object)s as int?;` - actually returns `null` – NSGaga-mostly-inactive Apr 08 '12 at 19:03
  • Ok, I think I've figured out what you're asking... Let's try this: it's not `(int?)`'s job to throw when `s` isn't an `int?`. Its job is to throw if `s` isn't _convertible_ to an `int?`. There are _many_ cases where `(type)`-style casting can do things other than just testing whether you've got that type (for example you can even define your own--http://msdn.microsoft.com/en-us/library/xhbhezf4(v=vs.100).aspx). If you really want to test to see if what you have is an actual instance of a type, you should use the `is` operator. – sblom Apr 08 '12 at 19:18
  • 1
    Or in other words, in the first example, `s as int?`, you're trying to convert `s` itself. In the second example, `(int?)s`, you take the _value_ of `s` and try to convert that. Does it make sense like that? – Mr Lister Apr 08 '12 at 19:21
  • @MrLister Are you telling me that `(int?)s` is a value operation ( as opposed to [as] which is reference operation ) ? – Royi Namir Apr 08 '12 at 19:23
  • @RoyiNamir, your paraphrase of MrLister is pretty close to correct. It runs the risk of being confusing, however, because "value" and "reference" here don't have anything to do with C#'s value vs reference types. – sblom Apr 08 '12 at 19:27
  • 1
    @RoyiNamir Try to compile `int i = 123 as int;` and the error will be "The as operator must be used with a reference type ('int' is a value type)". `int i = (int)123;` on the other hand, will compile perfectly fine. – Mr Lister Apr 08 '12 at 19:27

5 Answers5

32

In your first example, the as operator attempts to use the object s as an int?. Since int? isn't anywhere in the inheritance chain of short?, this operation fails.

In your second example, you're actually creating a new int? i with the value from short? s. This is a more generous operation, because it doesn't have to preserve the original s object on the left hand side.

The important point here is that as isn't allowed to do anything that doesn't preserve your object's identity. An explicit cast can.

Here's what the C# standard says about how the (int?) form works:

6.1.4 Implicit nullable conversions

Predefined implicit conversions that operate on non-nullable value types can also be used with nullable forms of those types. For each of the predefined implicit identity and numeric conversions that convert from a non-nullable value type S to a non-nullable value type T, the following implicit nullable conversions exist:

· An implicit conversion from S? to T?.

· An implicit conversion from S to T?.

Evaluation of an implicit nullable conversion based on an underlying conversion from S to T proceeds as follows:

· If the nullable conversion is from S? to T?:

o If the source value is null (HasValue property is false), the result is the null value of type T?.

o Otherwise, the conversion is evaluated as an unwrapping from S? to S, followed by the underlying conversion from S to T, followed by a wrapping (§4.1.10) from T to T?.

· If the nullable conversion is from S to T?, the conversion is evaluated as the underlying conversion from S to T followed by a wrapping from T to T?.

sblom
  • 26,911
  • 4
  • 71
  • 95
  • beyond this explanation theres still this line `(int?)s` which DO related to inheritance/boxing era....but it works and that is the thing which i dont understant. – Royi Namir Apr 08 '12 at 19:04
  • @RoyiNamir, I don't really understand. A conversion exists from `short` to `int` (that's true in C, Java, and pretty much everywhere else), and the C# spec describes how to take that conversion and make it work on nullable types. (I added the relevant excerpt above.) – sblom Apr 08 '12 at 19:13
  • 1
    @sblom I think you should `double-bold` the paragraph above saying `The important point here is that as isn't allowed to do anything that doesn't preserve your object's identity. An explicit cast can.` - I think that's the core to this. And as for casting allowed, I agree, that's clear to me (`as` wasn't really), and as Eric said there's no arithmetics on short-s (doesn't really matter to this but it's more easier to understand, to me at least, the conversion), it converts to int. – NSGaga-mostly-inactive Apr 08 '12 at 19:24
  • Actually the reason `as int?` doesn't compile is because that is not a valid syntax. C# does not attempt to allow `as` with nullable values; the compiler doesn't attempt to make such a cast possible. For example, I was thinking "I either have a 'boxed int', or I want the result to become 'null'", but the compiler doesn't handle that. – ToolmakerSteve May 26 '20 at 02:26
4

The example:

int? i = (int?)s;

Does compiler because a cast is you telling the compiler that you know something that it can't infer, that is, that s can be converted to a int?.

You will only get the exception at runtime if the cast is not successful.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
0

I think it's cause in case of as failure you will be given a "valid" null result, so false positive. In second case, cast is allowed cause in case of failure it will raise an exception.

Tigran
  • 61,654
  • 8
  • 86
  • 123
0

The reason is that int? is just shorthand for System.Nullable<int> (System.Nullable<T> is the type). The short type defines an explicit cast to an int, however System.Nullable<T> doesn't have any such explicit cast, because T could be any other value type.

Andy
  • 8,432
  • 6
  • 38
  • 76
0

I have found this possible solution via reflection. The idea is:

    var notNullableValue = 3;
    var someNullableType = typeof(Nullable<int>);
    var nullableValue = Activator.CreateInstance(someNullableType, new[] { notNullableValue });

In my case I could not use a direct cast because I was using arbitrary Types and Reflection to create an object and fill all properties.

Anton Semenov
  • 411
  • 5
  • 11