Tertiary Operator Confusion ( a = b ? c : d)

A

Andrew Robinson

I don't know if this is a compiler error or not. It all kind of makes sense
but you would think that the compiler would check the types of the possible
outcomes of the tertiary operator (c and d) against the type of the variable
being assigned (a). Instead it compares the types of c and d which may not
be compatible. Just seems silly.

int? i;
object o;

//this is ok:
if (i.HasValue)
o = i;
else
o = DBNull.Value;

//this fails to compile
//Error 1 Type of conditional expression cannot be determined because
//there is no implicit conversion between 'int?' and 'System.DBNull'

o = i.HasValue ? i : DBNull.Value;

//these also fail for the same reason

o = i == null ? DBNull.Value: i;
o = i.HasValue ? i : "ABC";

//but these works

o = i.HasValue ? (object)i : DBNull.Value;
o = i.HasValue ? (object)i : (object)DBNull.Value;
o = i.HasValue ? i : (object)DBNull.Value;
 
C

ClayB

One observation. It does seem that the compiler is enforcing the rule
that both alternatives must have the same type. This makes sense to me
when you consider that such tertiary expressions can be used in
complicated compound expressions where the ultimate target type is not
as obvious as it might be with a simple assignment statement.

==============
Clay Burch
Syncfusion, Inc.
 
B

Ben Voigt

ClayB said:
One observation. It does seem that the compiler is enforcing the rule
that both alternatives must have the same type. This makes sense to me
when you consider that such tertiary expressions can be used in
complicated compound expressions where the ultimate target type is not
as obvious as it might be with a simple assignment statement.

However all expressions in .NET have a common type, which is object.
Probably the current error should be replaced with a warning, with the
ternary expression resolved as the nearest common supertype which may be
object, or the union of object with some set of interfaces. The compiler
could then determine which interface is subsequently used and automatically
generate the cast.
 
J

Jon Skeet [C# MVP]

However all expressions in .NET have a common type, which is object.
Probably the current error should be replaced with a warning, with the
ternary expression resolved as the nearest common supertype which may be
object, or the union of object with some set of interfaces. The compiler
could then determine which interface is subsequently used and automatically
generate the cast.

Here are the rules which are actually used. Personally, I think it's
fine as it is:

<quote>
The second and third operands of the ?: operator control the type of
the conditional expression. Let X and Y be the types of the second and
third operands. Then,

*If X and Y are the same type, then this is the type of the
conditional expression.
*Otherwise, if an implicit conversion (§13.1) exists from X to Y,
but not from Y to X, then Y is the type of the conditional expression.
*Otherwise, if an implicit conversion (§13.1) exists from Y to X,
but not from X to Y, then X is the type of the conditional expression.
*Otherwise, no expression type can be determined, and a
compile-time error occurs.
</quote>

Jon
 
L

Laura T.

C# specification 2.0, 24.2.3:
... The compiler could then determine which interface is subsequently used
and automatically generate the cast.

"However, a nullable type never satisfies an interface constraint, even if
the underlying type implements the particular interface"
 
C

Christof Nordiek

Andrew Robinson said:
I don't know if this is a compiler error or not. It all kind of makes sense
but you would think that the compiler would check the types of the possible
outcomes of the tertiary operator (c and d) against the type of the
variable being assigned (a). Instead it compares the types of c and d which
may not be compatible. Just seems silly.

int? i;
object o;

//this is ok:
if (i.HasValue)
o = i;
else
o = DBNull.Value;

//this fails to compile
//Error 1 Type of conditional expression cannot be determined because
//there is no implicit conversion between 'int?' and 'System.DBNull'

o = i.HasValue ? i : DBNull.Value;

//these also fail for the same reason

o = i == null ? DBNull.Value: i;
o = i.HasValue ? i : "ABC";

//but these works

o = i.HasValue ? (object)i : DBNull.Value;
o = i.HasValue ? (object)i : (object)DBNull.Value;
o = i.HasValue ? i : (object)DBNull.Value;
BTW

you also could use the null coalescing operator:

o = i ?? (object)DBNull.Value

But still you have to cast to object.

Christof
 
C

Christof Nordiek

Onather remark:

if you cast the i to object, the result will be a boxed nullable int, not a
boxed int.
Could be an important difference.
 
M

Mark Wilden

It's actually a ternary operator, not a tertiary operator. Ternary refers to
the number of arguments. Tertiary would imply a ranking (primary, secondary,
tertiary).

///ark
 
J

Jon Skeet [C# MVP]

Mark Wilden said:
It's actually a ternary operator, not a tertiary operator. Ternary refers to
the number of arguments. Tertiary would imply a ranking (primary, secondary,
tertiary).

It's *a* ternary operator, and it's better described as *the*
conditonal operator. It happens to be the only ternary operator at the
moment (IIRC) but it's not guaranteed to be an unambiguous description
forever :)
 

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