The conditional operator gets confused, but why? [duplicate]
Assume two classes, both descendants of the same superclass, like this:
class MySuperClass{}
class A : MySuperClass{}
class B : MySuperClass{}
Then this assignment won't pass the compiler:
MySuperClass p = myCondition ? new A() : new B();
The compiler complains that A and B are not compatible (Type of conditional expression cannot be determined because there is no implicit conversion between 'A' and 'B' [CS0173] ). But they are both of type MySuperClass, so in my opinion this should work. Not that it's a big deal; a simple cast is all it takes to enlighten the compiler. But surely it's a snag in the C# compiler? Don't you agree?
The results of the conditional should be of the same type. They are not.
From MSDN, (?: Operator):
Either the type of first_expression and second_expression must be the same, or an implicit conversion must exist from one type to the other.
Since A
and B
are not the same type and you don't seem to have defined an implicit conversion, the compiler complains.
Look to section 7.14 of the language specification
The second and third operands, x and y, of the ?: operator control the type of the conditional expression.
· If x has type X and y has type Y then
o If an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
o If an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
o Otherwise, no expression type can be determined, and a compile-time error occurs.
· If only one of x and y has a type, and both x and y, of areimplicitly convertible to that type, then that is the type of the conditional expression.
· Otherwise, no expression type can be determined, and a compile-time error occurs.
Essentially, the operands must be convertible to one another, not mutually convertible to some other type.
It's why you need to make an explicit cast in your example or in cases such as nullables (int? foo = isBar ? 42 : (int?)null
). The declaration type does not impact the evaluation, the compiler must figure it out from the expression itself.
Check out this blog for some interesting articles on why C# compiler does/doesn't do things that are 'obvious' to you. The blog is written by Eric Lippert, one of the C# compiler developers.
The compiler doesn't try to look for a common ancestor, so you need an explicit cast to show which ancestor you wanted to treat it as; In your case:
MySuperClass p = myCondition ? (MySuperClass)(new A()) : (MySuperClass)(new B());
This means that the conditional operator has both sides returnin gthe same type, which satisfies the compiler.
The conditional operator (as with any other operator) has to define the type that its expression represents. In the case of the conditional operator, it has a two-step process:
- Are the operands of the same type? If so, that is the type of the expression.
- Is there an implicit conversion from one of the operand types to the other (but not in both directions)? If so, then the "other" is the type of the expression.
There's no ancestry search, as implementing that could lead down a slippery slope of ambiguity in what you, as a developer, could specify in that expression. Should everything lead to object
? What about value types, which would then be implicitly boxed? What about interfaces? If there's more than one common interface between the two types, which one should be chosen?
In your case, as you've discovered, you need to upcast one of the operands to the parent type. Once you do that, rule 2.) is satisfied (there is always an implicit conversion going from a more specific type to a less specific type).
Note that you only need to apply the cast to one of the operands, not both.
Rowland Shaw summed it up pretty well, but to see why a common ancestor isn't used implicitly, consider what would happen if both classes were also to implement a particular interface. The compiler wouldn't be able to work out which to use for the type of the conditional operator, and so it would be forced to use object
instead.
精彩评论