質問

I am wondering why this line of code doesn't compile:

ILogStuff Logger = (_logMode) ? new LogToDisc() : new LogToConsole();

Note that both classes LogToDiscand LogToConsole implement ILogStuff, and _logMode is a boolean variable. The error message I get is:

Error 3: Type of conditional expression cannot be determined because there is no implicit conversion between 'xxx.LogToDisc' and 'xxx.LogToConsole'

But why should there be one? What am I missing?

役に立ちましたか?

解決

There is not implicit conversion available for ternary operator. You need to cast the returned object by ternary operator to ILogStuff, This is very well explain in Eric Lippert's answer for the question Implicit conversion issue in a ternary condition

ILogStuff Logger = (_logMode) ? (ILogStuff) new LogToDisc() : (ILogStuff) new LogToConsole();

From chapter 7.13 of the C# Language Specification:

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 (§6.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 (§6.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.

他のヒント

You need to cast to the interface:

ILogStuff Logger = (_logMode) ? (ILogStuff)new LogToDisc() : new LogToConsole();

The specification describes the behaviour of the conditional operator:

7.14 Conditional operator

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

  • 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.
  • 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.
  • Otherwise, no expression type can be determined, and a compile-time error occurs.

There is no implicit conversion between LogToDisc and LogToConsole in either direction, so the compilation fails. If you fix one of the types to ILogStuff the implicit conversion from the other type will exist.

The message is correct, there is no implicit conversion between those two types, they just share the common interface. But of course shared parent do not imply possiblity of casting, in the same way as int is not implicty covertible into string although both have common parent - Object.

The ternary operator expects that the result type of both possible values will be the same - in terms of possibility to make an implicit cast between them. So you must tell him, that the first return value is of type ILogStuff:

ILogStuff Logger = (_logMode) ? (ILogStuff)new LogToDisc() : new LogToConsole();

Then, the second possible value is proper one - there exists implicit conversion between LogToConsole type and ILogStuff interface.

The expression must return a common type of both implementations. By explicitly casting the instances to the interface, the expression compiles:

ILogStuff Logger = (_logMode) ? 
    (ILogStuff)new LogToDisc() : 
    (ILogStuff)new LogToConsole();

Adil has provided the section that determines this behaviour, but I'd like to explain why this behaviour is sensible.

bool ? val1 : val2

This is an expression. Expressions need to have a type that is determinable at compile time. This makes it faster, and catches errors sooner.

Now, if:

val is an instance of MyObject1 which extends SomeParent and implements MyInterface,
and val2 is and instance of MyObject2 which extends SomeParent and implements MyInterface

How do we determine the compile-time type of this expression? We could try to find a common type between MyObject1 and MyObject2. What's the most obvious solution? Do you call it a SomeParent or a MyInterface? and If they had 2 Interfaces, which one would you pick?

The problem is this is a mess, and would require some pretty contrived rules(and in fact there are more examples that would be less clear) that at the end of the day, would be less intuitive than the current definition.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top