conditional operator throws exceptions when casting

W

Webbert

I wrote a little test to demonstrate.

Test 1 - should fail - can not cast null to an Int16
Test 2 - should result in the value 0 - one cast on the value returned
Test 3 - should result in the value 0 - cast on each return value
Test 4 - should result in the value 0 - normal cast
Test 5 - should result in the value 0 - Same as test 2 but hard coded

Any thoughts are appreciated.
Dave

============

Test 1: Failed: Object reference not set to an instance of an object.
Test 2: Failed: Specified cast is not valid.
Test 3: Successful: 0
Test 4: Successful: 0
Test 5: Successful: 0

============


using System;
using System.Data;
using System.Diagnostics;

namespace ConditionalCheck
{
class Program
{
static void Main( string[] args )
{
bool rc;
Int16 number;

// Setup
DataTable dt = new DataTable();
dt.Columns.Add( new DataColumn( "port", typeof( Int16 ) ) );
dt.Rows.Add();

Debug.WriteLine( "\n\n\n" );
Debug.WriteLine( "===================\n" );

// Test
object v = dt.Rows[0]["port"];

try { number = (Int16)( ( v != DBNull.Value ) ? v : null );
Debug.WriteLine( "Test 1: Successful: " + number ); }
catch ( Exception ex ) { Debug.WriteLine( "Test 1: Failed: " + ex.Message
); }

try { number = (Int16)( ( v != DBNull.Value ) ? v : 0 ); Debug.WriteLine(
"Test 2: Successful: " + number ); }
catch ( Exception ex ) { Debug.WriteLine( "Test 2: Failed: " + ex.Message
); }

try { number = ( ( v != DBNull.Value ) ? (Int16)v : (Int16)0 );
Debug.WriteLine( "Test 3: Successful: " + number ); }
catch ( Exception ex ) { Debug.WriteLine( "Test 3: Failed: " + ex.Message
); }

number = 0;
object y = number;
try { number = (Int16)( y ); Debug.WriteLine( "Test 4: Successful: " +
number ); }
catch ( Exception ex ) { Debug.WriteLine( "Test 4: Failed: " + ex.Message
); }

rc = ( number == 500 );
try { number = (Int16)( ( rc ) ? 1 : 0 ); Debug.WriteLine( "Test 5:
Successful: " + number ); }
catch ( Exception ex ) { Debug.WriteLine( "Test 5: Failed: " + ex.Message
); }


Debug.WriteLine( "\n===================\n" );
}
}
}
 
J

Jon Skeet [C# MVP]

Webbert said:
I wrote a little test to demonstrate.

Test 1 - should fail - can not cast null to an Int16
Test 2 - should result in the value 0 - one cast on the value returned
Test 3 - should result in the value 0 - cast on each return value
Test 4 - should result in the value 0 - normal cast
Test 5 - should result in the value 0 - Same as test 2 but hard coded

Any thoughts are appreciated.

The problem is that the cast is being applied to "object", as that's
the type of v. Now, the cast is to Int16, but the literal 0 ends up as
a boxed Int32. It's equivalent to:

object o = 0;
Int16 x = (Int16) o;

which will also fail.

Change the literal to (Int16)0 and it should be okay.
 
W

Webbert

Understood.

I guess I just don't understand why 0 (zero) is getting boxed to the type of
v before the cast. I would have expected the literal type to be used.

Thanks,
Dave
 
P

Peter Duniho

Understood.

I guess I just don't understand why 0 (zero) is getting boxed to the
type of v before the cast. I would have expected the literal type to be
used.

The literal type is used, just not when you are expecting it to.

The "?:" expression needs to have a single type. The type is generated at
compile time, and if you have two different types in the result operands
for the operator, one needs to be implicitly castable by the compiler to
the type of the other, with the type that winds up being suitable for the
cast being applied to the whole expression.

Since the result operands have type "object" and type "Int32", and because
Int32 is implicitly castable to object but object is not implicitly
castable to Int32, the type of the whole expression is object and the
Int32 parameter must be cast to object before evaluating the expression.
The implicit cast simply boxes the Int32, which of course is cannot be
successfully unboxed to an Int16.

As Jon says, you can resolve the issue by providing an explicit type for
the literal by casting it to Int16. That way when it gets boxed to suit
the expression's overall type, it's a type that can be cast back to Int16.

Pete
 
W

Webbert

Jon/Peter,

Thanks for the input.

I have a tendency to want to know why. The underlying piece that I was not
aware of was that the conditional operator needs to have a single type at
compile time. With that in mind I understand exactly why the cast was
failing.

Perfectly clear now.

Thanks,
Dave
 
P

Peter Duniho

Jon/Peter,

Thanks for the input.

I have a tendency to want to know why.

An attitude like that should serve you well. :)
The underlying piece that I was not
aware of was that the conditional operator needs to have a single type at
compile time.

I thought as much. It's a natural requirement IMHO, but not one that's
necessarily obvious at first glance. I've overlooked it myself in the
past. (It does become painfully obvious when the two result operands
cannot be implicitly cast from one to the other, but it's harder to write
code like that by accident. :) )
With that in mind I understand exactly why the cast was
failing.

Perfectly clear now.

Excellent. Glad to hear we could help!

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top