If var keyword is resolved at compile time, how does the following work?
class A {
}
class B : A {
}
int k = 1;
var x = (k < 0) ? new B() : new A();
Edit:
I finally understood that the problem is not about the var itself, but about the behaviour of the ?: operator. For some reason, I thought that the following could be possible:
object x = something ? 1 : ""
and that's not possible at all :)
Related question (about ternary operator):
Why assigning null in ternary operator fails: no implicit conversion between null and int?
The result is of type A, because both of the variables are of type A, and at least one of them is directly of type A (not through some conversion).
The compiler takes a look at both parts of the ternary expression, and if one of them is a subtype of the other, the entire expression becomes the more general supertype.
However, if neither is directly of the common type, then a compiler error occurs, probably because it doesn't know how much to upcast for you (and it doesn't feel like finding out).
See here:
The conditional operator (?:) returns one of two values depending on the value of a Boolean expression. Following is the syntax for the conditional operator.
condition ? first_expression : second_expression;
[...]
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.
The result is A. An easy way to confirm it is to place your mouse over the var.
I haven't tested this degenerate case. But I would bet either (1) compiler complains or (2) 'x' is of type 'A'.
Related
why can't I have something like:
Some_Function ? myList.Add(a) : throw new Exception();
Why can't I throw exception in else part of ?: operator? what is the main purpose of ?: operator?
Suggestion
If anybody else wondered about the same thing, beside reading the answers below, i suggest that you read this post as well..
Expression Versus Statement
The other answers and comments are of course true (we can refer to the documentation), though my understanding of the question is more like, "why does it have to be that way?".
According to the C# 5.0 specification, the conditional operator forms an expression, not a statement. I suspect that the reason why it's not a statement, thus preventing you from doing something like a ? b() : throw e is simply because we already have a statement construct for achieving basically the same thing; namely, if..else.
if (a) { b(); } else { throw e; }
The benefit of the conditional operator is that it can be used within statements or other expressions.
bool? nb = GetValue();
if (nb ?? (a ? b() : c())) { throw e; }
Because it is defined as:
null-coalescing-expression ? expression : expression
As you can read in ?: Operator (C# Reference):
The conditional operator (?:) returns one of two values depending on the value of a Boolean expression.
One of both expression is evaluated depending on the boolean value of null-coalescing-expression.
Since neither of your two code fragments are expressions that evaluate to a value (List.Add() is void and an exception doesn't evaluate to a value either), this can't compile.
You need to remember that Ternary operator must return something and List.Add is a void method so it fails. Both the sides must be compatible and should return something.
The MSDN says:
The condition must evaluate to true or false. If condition is true,
first_expression is evaluated and becomes the result. If condition is
false, second_expression is evaluated and becomes the result.
And myList.Add(a) is not an expression.
See ?: Operator on MSDN. Its clearly explained there.
The conditional operator (?:) returns one of two values depending on the value of a Boolean expression. Following is the syntax for the conditional operator.
condition ? first_expression : second_expression;
The condition must evaluate to true or false. If condition is true, first_expression is evaluated and becomes the result. If condition is false, second_expression is evaluated and becomes the result. Only one of the two expressions is evaluated.
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.
You can express calculations that might otherwise require an if-else construction more concisely by using the conditional operator. For example, the following code uses first an if statement and then a conditional operator to classify an integer as positive or negative.
The conditional operator (?:) returns one of two values depending on the value of a Boolean expression. Following is the syntax for the conditional operator.
The condition must evaluate to true or false. If condition is true, first_expression is evaluated and becomes the result. If condition is false, second_expression is evaluated and becomes the result. Only one of the two expressions is evaluated.
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.
You can express calculations that might otherwise require an if-else construction more concisely by using the conditional operator. For example, the following code uses first an if statement and then a conditional operator to classify an integer as positive or negative.
This question already has answers here:
Implicit conversion issue in a ternary condition [duplicate]
(4 answers)
Closed 8 years ago.
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.
For example, given this code:
int? ReallyComplexFunction()
{
return 2; // Imagine this did something that took a while
}
void Something()
{
int i = ReallyCleverFunction() ?? 42;
}
... is it guaranteed that the function will only be called once? In my test it's only called once, but I can't see any documentation stating I can rely on that always being the case.
Edit
I can guess how it is implemented, but c'mon: we're developers. We shouldn't be muddling through on guesses and assumptions. For example, will all future implementations be the same? Will another platform's implementation of the language be the same? That depends on the specifications of the language and what guarantees it offers. For example, a future or different platform implementation (not a good one, but it's possible) may do this in the ?? implementation:
return ReallyComplexFunction() == null ? 42 : ReallyComplexFunction();
That, would call the ReallyComplexFunction twice if it didn't return null. (although this looks a ridiculous implementation, if you replace the function with a nullable variable it looks quite reasonable: return a == null ? 42 : a)
As stated above, I know in my test it's only called once, but my question is does the C# Specification guarantee/specify that the left-hand side will only be called once? If so, where? I can't see any such mention in the C# Language Specification for ?? operator (where I originally looked for the answer to my query).
The ?? operator will evaluate the left side once, and the right side zero or once, depending on the result of the left side.
Your code
int i = ReallyCleverFunction() ?? 42;
Is equivalent to (and this is actually very close to what the compiler actually generates):
int? temp = ReallyCleverFunction();
int i = temp.HasValue ? temp.Value : 42;
Or more simply:
int i = ReallyCleverFunction().GetValueOrDefault(42);
Either way you look at it, ReallyCleverFunction is only called once.
The ?? operator has nothing to do with the left hand. The left hand is run first and then the ?? operator evaluates its response.
In your code, ReallyCleverFunction will only run once.
It will be only called once. If the value of ReallyCleverFunction is null, the value 42 is used, otherwise the returned value of ReallyCleverFunction is used.
It will evaluate the function, then evaluate the left value (which is the result of the function) of the ?? operator. There is no reason it would call the function twice.
It is called once and following the documentation I believe this should be sufficient to assume it is only called once:
It returns the left-hand operand if the operand is not null; otherwise
it returns the right operand.
?? operator
It will only be run once because the ?? operator is just a shortcut.
Your line
int i = ReallyCleverFunction() ?? 42;
is the same as
int? temp = ReallyCleverFunction();
int i;
if (temp != null)
{
i = temp.Value;
} else {
i = 42;
}
The compiler does the hard work.
Firstly, the answer is yes it does guarantee it will only evaluate it once, by inference in section 7.13 of the official C# language specification.
Section 7.13 always treats a as an object, therefore it must only take the return of a function and use that in the processing. It says the following about the ?? operator (emphasis added):
• If b is a dynamic expression, the result type is dynamic. At run-time, a is first evaluated. If a is not null, a is converted to dynamic, and this becomes the result. Otherwise, b is evaluated, and this becomes the result.
• Otherwise, if A exists and is a nullable type and an implicit conversion exists from b to A0, the result type is A0. At run-time, a is first evaluated. If a is not null, a is unwrapped to type A0, and this becomes the result. Otherwise, b is evaluated and converted to type A0, and this becomes the result.
• Otherwise, if A exists and an implicit conversion exists from b to A, the result type is A. At run-time, a is first evaluated. If a is not null, a becomes the result. Otherwise, b is evaluated and converted to type A, and this becomes the result.
• Otherwise, if b has a type B and an implicit conversion exists from a to B, the result type is B. At run-time, a is first evaluated. If a is not null, a is unwrapped to type A0 (if A exists and is nullable) and converted to type B, and this becomes the result. Otherwise, b is evaluated and becomes the result.
As a side-note the link given at the end of the question is not the C# language specification despite it's first ranking in a Google Search for the "C# language specification".
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Conditional operator cannot cast implicitly?
Why does null need an explicit type cast here?
I've had a search and haven't found a good explanation for why the following occurs.
I have two classes which have an interface in common and I have tried initializing an instance of this interface type using the ternary operator as below but this fails to compile with the error "Type of conditional expression cannot be determined because there is no implicit conversion between 'xxx.Class1' and 'xxx.Class2':
public ConsoleLogger : ILogger { .... }
public SuppressLogger : ILogger { .... }
static void Main(string[] args)
{
.....
// The following creates the compile error
ILogger logger = suppressLogging ? new SuppressLogger() : new ConsoleLogger();
}
This works if I explicitly cast the first conditioin to my interface:
ILogger logger = suppressLogging ? ((ILogger)new SuppressLogger()) : new ConsoleLogger();
and obviously I can always do this:
ILogger logger;
if (suppressLogging)
{
logger = new SuppressLogger();
}
else
{
logger = new ConsoleLogger();
}
The alternatives are fine but I can't quite get my head around why the first option fails with the implicit conversion error as, in my view, both classes are of type ILogger and I am not really looking to do a conversion (implicit or explicit). I'm sure this is probably a static language compilation issue but I would like to understand what is going on.
This is a consequence of the confluence of two characteristics of C#.
The first is that C# never "magics up" a type for you. If C# must determine a "best" type from a given set of types, it always picks one of the types you gave it. It never says "none of the types you gave me are the best type; since the choices you gave me are all bad, I'm going to pick some random thing that you did not give me to choose from."
The second is that C# reasons from inside to outside. We do not say "Oh, I see you are trying to assign the conditional operator result to an ILogger; let me make sure that both branches work." The opposite happens: C# says "let me determine the best type returned by both branches, and verify that the best type is convertible to the target type."
The second rule is sensible because the target type might be what we are trying to determine. When you say D d = b ? c : a; it is clear what the target type is. But suppose you were instead calling M(b?c:a)? There might be a hundred different overloads of M each with a different type for the formal parameter! We have to determine what the type of the argument is, and then discard overloads of M which are not applicable because the argument type is not compatible with the formal parameter type; we don't go the other way.
Consider what would happen if we went the other way:
M1( b1 ? M2( b3 ? M4( ) : M5 ( ) ) : M6 ( b7 ? M8() : M9() ) );
Suppose there are a hundred overloads each of M1, M2 and M6. What do you do? Do you say, OK, if this is M1(Foo) then M2(...) and M6(...) must be both convertible to Foo. Are they? Let's find out. What's the overload of M2? There are a hundred possibilities. Let's see if each of them is convertible from the return type of M4 and M5... OK, we've tried all those, so we've found an M2 that works. Now what about M6? What if the "best" M2 we find is not compatible with the "best" M6? Should we backtrack and keep on re-trying all 100 x 100 possibilities until we find a compatible pair? The problem just gets worse and worse.
We do reason in this manner for lambdas and as a result overload resolution involving lambdas is at least NP-HARD in C#. That is bad right there; we would rather not add more NP-HARD problems for the compiler to solve.
You can see the first rule in action in other place in the language as well. For example, if you said: ILogger[] loggers = new[] { consoleLogger, suppressLogger }; you'd get a similar error; the inferred array element type must be the best type of the typed expressions given. If no best type can be determined from them, we don't try to find a type you did not give us.
Same thing goes in type inference. If you said:
void M<T>(T t1, T t2) { ... }
...
M(consoleLogger, suppressLogger);
Then T would not be inferred to be ILogger; this would be an error. T is inferred to be the best type amongst the supplied argument types, and there is no best type amongst them.
For more details on how this design decision influences the behaviour of the conditional operator, see my series of articles on that topic.
If you are interested in why overload resolution that works "from outside to inside" is NP-HARD, see this article.
You can do that:
ILogger logger = suppressLogging ? (ILogger)(new SuppressLogger()) : (ILogger)(new ConsoleLogger());
When you have an expression like condition ? a : b, there must be an implicit conversion from the type of a to the type of b, or the other way round, otherwise the compiler can't determine the type of the expression. In your case, there is no conversion between SuppressLogger and ConsoleLogger...
(see section 7.14 in the C# 4 language specifications for details)
The problem is that the right hand side of the statement is evaluated without looking at the type of the variable it is assigned to.
There's no way the compiler can look at
suppressLogging ? new SuppressLogger() : new ConsoleLogger();
and decide what the return type should be, since there's no implicit conversion between them. It doesn't look for common ancestors, and even if it did, how would it know which one to pick.
Any time you change a variable of one type into a variable of another type, that's a conversion. Assigning an instance of a class to a variable of any type other than that class requires a conversion. This statement:
ILogger a = new ConsoleLogger();
will perform an implicit conversion from ConsoleLogger to ILogger, which is legal because ConsoleLogger implements ILogger. Similarly, this will work:
ILogger a = new ConsoleLogger();
ILogger b = suppress ? new SuppressLogger() : a;
because there is an implicit conversion between SuppressLogger and ILogger. However, this won't work:
ILogger c = suppress ? new SuppressLogger() : new ConsoleLogger();
because the tertiary operator will only try so hard to figure out what type you wanted in the result. It essentially does this:
If the types of operands 2 and 3 are the same, the tertiary operator returns that type and skips the rest of these steps.
If operand 2 can be implicitly converted to the same type as operand 3, it might return that type.
If operand 3 can be implicitly converted to the same type as operand 2, it might return that type.
If both #2 and #3 are true, or neither #2 or #3 are true, it generates an error.
Otherwise, it returns the type for whichever of #2 or #3 was true.
In particular, it will not start searching through all the types it knows about looking for a "least common denominator" type, such as an interface in common. Also, the tertiary operator is evaluated, and its return type deterimined, independant of the type of variable you are storing the result into. It's a two step process:
Determine the type of the ?: expression and calculate it.
Store the result of #1 into the variable, performing any implicit conversions as needed.
Typecasting one or both of your operands is the correct way to perform this operation if that's what you need.
This question already has answers here:
Nullable types and the ternary operator: why is `? 10 : null` forbidden? [duplicate]
(9 answers)
Conditional operator cannot cast implicitly?
(3 answers)
Closed 9 years ago.
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.