【问题标题】:Ternary Operator syntax to choose implementation of Interface [duplicate]三元运算符语法选择接口的实现[重复]
【发布时间】:2014-05-31 01:17:26
【问题描述】:

我想知道为什么这行代码不能编译:

ILogStuff Logger = (_logMode) ? new LogToDisc() : new LogToConsole();

请注意,LogToDiscLogToConsole 两个类都实现了ILogStuff,而_logMode 是一个布尔变量。我得到的错误信息是:

错误3:无法确定条件表达式的类型,因为'xxx.LogToDisc'和'xxx.LogToConsole'之间没有隐式转换

但是为什么要有一个呢?我错过了什么?

【问题讨论】:

  • 使用ReSharper 甚至可以让你不用在这里问这个问题;-)
  • 是的,但是据我所知,ReSharper 不是免费软件...
  • 那又怎样?支付几百美元(IMO)比在 Stack Overflow 上花费半小时并在工作时间提问更便宜。
  • 好吧,如果你是一家大公司的专业程序员,也许可以。但是我发现您的评论对像我这样的所有免费软件/非商业程序员有点不尊重。此外,如果某些工具会向我显示错误,我仍然没有像现在这样学到很多东西,即为什么它会起作用和不应该起作用的具体原因。

标签: c# ternary-operator


【解决方案1】:

没有可用于三元运算符的隐式转换。您需要通过三元运算符将返回的对象转换为ILogStuff,这在 Eric Lippert 对问题Implicit conversion issue in a ternary condition 的回答中得到了很好的解释

ILogStuff Logger = (_logMode) ? (ILogStuff) new LogToDisc() : (ILogStuff) new LogToConsole();

来自 C# 语言规范的第 7.13 章:

?: 运算符的第二个和第三个操作数控制条件表达式的类型。设 X 和 Y 是第二个和第三个操作数的类型。那么,

  • 如果 X 和 Y 是同一类型,则这是条件表达式的类型。
  • 否则,如果存在从 X 到 Y 而不是从 Y 到 X 的隐式转换(第 6.1 节),则 Y 是条件表达式的类型。
  • 否则,如果存在从 Y 到 X 而不是从 X 到 Y 的隐式转换(第 6.1 节),则 X 是条件表达式的类型。
  • 否则无法确定表达式类型,并出现编译时错误。

【讨论】:

  • 感谢您的链接!它甚至以 Logger 为例,lol ...
【解决方案2】:

你需要转换到接口:

ILogStuff Logger = (_logMode) ? (ILogStuff)new LogToDisc() : new LogToConsole();

规范描述了条件运算符的行为:

7.14 条件运算符

?: 操作符的第二个和第三个操作数 x 和 y 控制 条件表达式的类型。

如果 x 具有 X 类型并且 y 具有类型 是的话

  • 如果存在从 X 到 Y 的隐式转换(第 6.1 节),但 不是从 Y 到 X,那么 Y 是条件表达式的类型。
  • 如果存在从 Y 到 X 的隐式转换(第 6.1 节),但不存在从 X 到 Y,那么 X 就是条件表达式的类型。
  • 否则,无法确定表达式类型,并且 发生编译时错误。

LogToDiscLogToConsole之间没有任何方向的隐式转换,所以编译失败。如果您将其中一种类型修复为ILogStuff,则将存在从另一种类型的隐式转换。

【讨论】:

  • 坦率地说,我认为没有理由不同时使用它们。它更具可读性。
【解决方案3】:

消息是正确的,这两种类型之间没有隐式转换,它们只是共享通用接口。但是当然共享父级并不意味着可以进行强制转换,就像int 不隐含可转换为string 一样,尽管两者都有共同的父级 - Object

三元运算符期望两个可能值的结果类型相同 - 就在它们之间进行隐式转换的可能性而言。所以你必须告诉他,第一个返回值的类型是ILogStuff

ILogStuff Logger = (_logMode) ? (ILogStuff)new LogToDisc() : new LogToConsole();

那么,第二个可能的值是正确的——LogToConsole 类型和ILogStuff 接口之间存在隐式转换。

【讨论】:

  • 是的,明白你在说什么。谢谢。当ILogStuff Logger = new LogToConsole() 编译得很好时,这似乎很奇怪,并且您认为您使用三元表达式只是为了选择一个具体的类。在这个逻辑中,他们不需要彼此有任何关系,除了实现ILogStuff
  • @ToaoG 关键是它是一个表达式。无论您将其分配给什么,该表达式都需要自己有意义。表达式在编译时应该有一个可确定的类型。
【解决方案4】:

表达式必须返回两种实现的通用类型。通过将实例显式转换为接口,表达式可以编译:

ILogStuff Logger = (_logMode) ? 
    (ILogStuff)new LogToDisc() : 
    (ILogStuff)new LogToConsole();

【讨论】:

    【解决方案5】:

    Adil 提供了确定这种行为的部分,但我想解释一下为什么这种行为是明智的。

    bool ? val1 : val2
    

    这是一个表达式。表达式需要具有可在编译时确定的类型。这使它更快,并更快地发现错误。

    现在,如果:

    val 是MyObject1 的一个实例,它扩展了SomeParent 并实现了MyInterface
    val2 是 MyObject2 的实例,它扩展了 SomeParent 并实现了 MyInterface

    我们如何确定这个表达式的编译时类型?我们可以尝试在MyObject1MyObject2 之间找到一个共同的类型。最明显的解决方案是什么?你叫它SomeParent 还是MyInterface?如果他们有 2 个接口,你会选择哪一个?

    问题是这是一团糟,并且需要一些相当人为的规则(实际上还有更多的例子不太清楚),最终会比当前的定义更不直观。

    【讨论】:

      猜你喜欢
      • 2015-03-25
      • 1970-01-01
      • 2016-09-05
      • 2019-12-30
      • 2015-09-07
      • 2021-08-07
      • 1970-01-01
      • 2021-03-25
      • 2019-07-14
      相关资源
      最近更新 更多