Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 780
Sam Leach Avatar asked Jan 07 '15 14:01

Sam Leach


2 Answers

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.

like image 74
Yuval Itzchakov Avatar answered Nov 06 '22 04:11

Yuval Itzchakov


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
like image 31
dcastro Avatar answered Nov 06 '22 04:11

dcastro