【问题标题】:Understanding C# compilation error with ternary operator使用三元运算符了解 C# 编译错误
【发布时间】:2012-12-18 02:57:56
【问题描述】:

我从Wrox Professional ASP.NET 4.0 MVC 4 书第 179 页(“了解 Web 应用程序中的安全向量”一章)中复制了以下代码,并稍加修改,将其设置为 protected 并将其作为实用程序方法存储在我的抽象应用程序中 -宽Controller

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

上面的代码旨在保护 MVC 应用程序免受开放重定向攻击,这不是问题的主题。

代码显然格式正确,可以编译,我相信它可以工作。

当“巧妙地”将上面的代码更改为以下单行代码时会出现问题

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

上面的单行代码应该和扩展代码完全一样(不,ReSharper 没有建议我替换,这是我的倡议)。

编译错误如下:there is no implicit conversion between System.Web.Mvc.RedirectResult and System.Web.Mvc.RedirectToRouteResult.

ReSharper 随后会提供帮助并提出以下修改建议

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

问题是“为什么我必须强制转换重定向方法”?

由于RedirectRedirectToAction 都返回ActionResult 的子类(通过F12 验证)并且该子类是函数的返回值,因此它应该是自动兼容的。或者至少,根据我对 C# 的了解,两种代码都可以编译,或者它们都不能编译。

为了更笼统,问题可以重新表述如下:

假设我有 A、B 和 C 类

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

假设以下函数有效

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

为什么下面的单行代码不能编译?

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

【问题讨论】:

标签: c# inheritance if-statement ternary-operator


【解决方案1】:

编译器在条件语句中期望相同的返回类型,因此它期望这两个是相同的类型。如果您将选项转换为 ActionResult,它将起作用。您可以通过只转换最后一个来与编译器相处。

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

【讨论】:

  • +1 当我阅读原始问题时,我猜这将成为妙语。理论上 c# 可以允许它工作,但它只会在某些情况下工作,因此会令人困惑(我假设 c++ ?: 是一样的)。
【解决方案2】:

它不会编译,因为编译器会根据两个返回值之一来决定单行的返回类型是什么。由于类型 B 不是从类型 C 派生的,或者相反,这两种类型不是互斥的,并且 if (作为一个整体)的返回类型不能以这种方式派生。

如果您想在单行中执行此操作,则必须将返回值强制转换为您要返回的类型,即类型 A。

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

更新:我没有看到你已经在你的问题中做了演员,我很抱歉。

【讨论】:

  • 补充说明(后人):编译器为了验证one-liner的表达式是否与return语句兼容,编译器必须将one-liner返回的类型与函数声明相匹配。它开始检查三元运算符以确定它们返回的类型。即使 B 和 C 都扩展了 A 并且即使任何东西扩展了 C# 中的对象,编译器也无法做出具体的决定,如果不存在隐式转换或代码中没有放置显式转换
  • 所以扩展代码有效,因为“return C”表达式也与“protected A”和“return B”兼容。但是在三元表达式的情况下,编译器根本不会努力爬回 B 和 C 的继承树来找到共同的祖先(这是返回兼容的)。这是为了使编译器高效且易于维护而设计的:编译器必须直接确定该三元运算符返回的类型,将其视为原子表达式
  • 精湛的附加解释,@djechelon
猜你喜欢
  • 2017-05-12
  • 2015-09-21
  • 2012-08-13
  • 1970-01-01
  • 2014-12-14
  • 2012-05-03
  • 1970-01-01
  • 2014-06-16
  • 2021-07-07
相关资源
最近更新 更多