Understanding C# compilation error with ternary op

2019-01-26 21:06发布

I have copied the following code from Wrox Professional ASP.NET 4.0 MVC 4 book, page 179 (Chapter "Understanding the Security Vectors in a Web Application") with the little modification of making it protected and storing as utility method in my abstract application-wide Controller

protected ActionResult RedirectToLocal(string returnUrl)
{
    if (Url.IsLocalUrl(returnUrl))
    {
        return Redirect(returnUrl);
    }
    else
    {
        return RedirectToAction("Index", "Home");
    }
}

The code above is aimed at securing the MVC application from open redirection attacks, which are not subject of the question.

The code is obviously well-formed, compiles and I trust it works.

The problem arises when "smartly" changing the code above into the following one-liner

return (Url.IsLocalUrl(returnUrl)) ? Redirect(returnUrl) : RedirectToAction("Index", "Home");

The one-liner above is supposed to do exactly the same as the extended code (no, ReSharper didn't suggest me to replace, it was my initiative).

The compilation error is the following: there is no implicit conversion between System.Web.Mvc.RedirectResult and System.Web.Mvc.RedirectToRouteResult.

ReSharper then comes to help and suggests the following modification

return (Url.IsLocalUrl(returnUrl)) ? (ActionResult) Redirect(returnUrl) : RedirectToAction("Index", "Home");

The question is "why do I have to cast Redirect method"?

Since both Redirect and RedirectToAction return a subclass of ActionResult (verified via F12) and that subclass is the return value of the function, it should be automatically compatible. Or at least, in my knowledge of C#, either both codes compile or they both don't.

To be more general, the question could be reformulated as follows:

Suppose I have class A, B and C

abstract class A {}
class B: A{}
class C: A{}

And suppose the following function works

private A Function(){
    if (condition) return new B();
    else return new C();
}

Why the following one-liner doesn't compile?

private A Function(){
    return (condition) ? new B(): new C();
}

2条回答
我命由我不由天
2楼-- · 2019-01-26 21:39

The compiler expects the same return type in a conditional statement, so it expects both of these to be the same type. If you cast the options to ActionResult, it will work. You may be able to get along with the compiler by only casting the last one.

return (Url.IsLocalUrl(returnUrl)) ? (ActionResult)Redirect(returnUrl) : (ActionResult)RedirectToAction("Index", "Home");
查看更多
做自己的国王
3楼-- · 2019-01-26 21:42

It doesn't compile because the compiler decides what the return-type of the one-liner is based upon one of the two return-values. Since type B doesn't derive of type C, or the other way around, the two types are not mutually exclusive, and the return type of the if (as a whole) cannot be derived that way.

If you would want to do it in a one-liner, you have to cast the return-values to the type that you want to return, which is type A.

private A Function(){
    return (condition) ? ((A)new B()): ((A)new C());
}

UPDATE: I didn't see that you already do the casting in your question, my apologies.

查看更多
登录 后发表回答