"Cannot be determined because there is no implicit conversion" with ternery if return
Asked Answered
S

3

17

I have the following ASP.NET Web Api 2 action with a ternary if return:

[HttpDelete]
public IHttpActionResult Delete()
{
    bool deleted;

    // ...

    return deleted ? this.Ok() : this.NotFound();
}

I receive a

Type of conditional expression cannot be determined because there is no implicit conversion between 'System.Web.Http.Results.OkResult' and 'System.Web.Http.Results.NotFoundResult'

when they both implement IHttpActionResult.

However if I remove the ternary if, the compiler is happy:

if (deleted)
{
    return this.Ok();
}
return this.NotFound();

Why is this?

Socorrosocotra answered 7/1, 2015 at 14:48 Comment(0)
S
21

You need to explicitly cast the result to IHttpActionResult:

return deleted ? (IHttpActionResult) this.Ok() : this.NotFound();

Edit:

As for Grants question:

Why does Sam's second block of code work without explicitly casting to IHttpActionResult, just out of curiosity? Is this something particular to the conditional ?: operator?

Lets create a simple demonstration. Assume the following code:

public interface IFoo { }

public class B : IFoo { }

public class C : IFoo { }

And then the following:

public class A
{
    IFoo F(bool b)
    {
        return b ? (IFoo) new B() : new C();
    }
}

Lets see how the compiler de-compiles the ternary operator:

private IFoo F(bool b)
{
    IFoo arg_13_0;
    if (!b)
    {
        IFoo foo = new C();
        arg_13_0 = foo;
    }
    else
    {
        arg_13_0 = new B();
    }
    return arg_13_0;
}

The explicit cast is enough for the compiler to infer that the variable should be of type IFoo and hence satisfy our whole if-else. That is why it is enough for us to "hint" the compiler only once of our type cast.

@dcastro has referenced the exact part of the language specification which determines the control of the type, see that for text-book definition.

Starkey answered 7/1, 2015 at 14:49 Comment(5)
@GrantWinney Yes it isWhitening
@GrantWinney the bottom line is there may be other types that both type implements so you need to tell the compiler which type do you want to cast. for example if they both implement IFoo interface casting that would be also valid.this is not the case in if statement because compiler knows the return type.Fickle
@GrantWinney I meant if you dont cast, there is no way for ternary to determine the return type of the expression. assume they both implement IBar and IFoo, should return type be IBar or IFoo? compiler can't know that and can't pick a random type.Fickle
@GrantWinney It's easier to understand what's going on if you forget about the method's return type entirely. The expression is evaluated independently. The question is, if a and b implemented both IFoo and IBar, what type would x be in this statement: var x = bool? a : bWhitening
This doesn't make sense as casting both to IActionResult works....which resharper claims (rightfully so) is a redundant cast.Mummy
W
8

In a ternary expression A? B : C, there must be a reference conversion (e.g., from a base type to a derived type or vice-versa) from either B to C or C to B.

You expected the compiler to find the most derived common ancestor of the two types (which is IHttpActionResult) - the compiler doesn't do that.

As a general rule of thumb, the type of the result of any expression must be contained within the expression itself. I.e., bool? dog : cat cannot return an animal because no variable of type animal is part of the expression.

From the C# Language Specification section 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
Whitening answered 7/1, 2015 at 14:56 Comment(0)
B
1

Starting with C# 9.0, the OP's example would compile successfully, because the result type of a ternary conditional expression is now based on the target type. Before, it was based on the operands' types; they had to be equal, or one operand had to be implicitly convertible to another.

Bibliomancy answered 15/11, 2021 at 17:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.