我想知道为什么这行代码不能编译:
ILogStuff Logger = (_logMode) ? new LogToDisc() : new LogToConsole();
请注意,LogToDisc
和LogToConsole
类都实现了ILogStuff
,而_logMode
是一个布尔变量。我得到的错误信息是:
错误3:无法确定条件表达式的类型,因为'xxx.LogToDisc'和'xxx.LogToConsole'之间没有隐式转换
但为什么要有一个呢?我错过了什么?
答案 0 :(得分:15)
三元运算符没有可用的隐式转换。你需要通过三元运算符将返回的对象强制转换为ILogStuff
,这在Eric Lippert对问题{(3}}
ILogStuff Logger = (_logMode) ? (ILogStuff) new LogToDisc() : (ILogStuff) new LogToConsole();
从C#语言规范的第7.13章开始:
?:运算符的第二个和第三个操作数控制条件表达式的类型。设X和Y是第二个和第三个操作数的类型。然后,
- 如果X和Y是相同的类型,则这是条件表达式的类型。
- 否则,如果从X到Y存在隐式转换(第6.1节),而不是从Y到X,则Y是条件表达式的类型。
- 否则,如果从Y到X存在隐式转换(第6.1节),而不是从X到Y,则X是条件表达式的类型。
- 否则,无法确定表达式类型,并发生编译时错误。
答案 1 :(得分:7)
你需要转换到界面:
ILogStuff Logger = (_logMode) ? (ILogStuff)new LogToDisc() : new LogToConsole();
规范描述了条件运算符的行为:
7.14条件运算符
?:运算符的第二个和第三个操作数x和y控制 条件表达式的类型。
如果x具有类型X且y具有类型 Y然后
- 如果从X到Y存在隐式转换(第6.1节),但是 不是从Y到X,那么Y是条件表达式的类型。
- 如果存在从Y到X的隐式转换(第6.1节),而不是来自 X到Y,则X是条件表达式的类型。
- 否则,无法确定表达式类型,并且a 发生编译时错误。
LogToDisc
和LogToConsole
之间没有任何方向的隐式转换,因此编译失败。如果您将其中一种类型修改为ILogStuff
,则将存在来自其他类型的隐式转换。
答案 2 :(得分:3)
消息是正确的,这两种类型之间没有隐式转换,它们只是共享公共接口。但当然共享父母并不意味着投射的可能性,就像int
并不是隐含的可转换为string
一样,尽管两者都有共同的父母 - Object
。
三元运算符期望两个可能值的结果类型将相同 - 就在它们之间进行隐式转换的可能性而言。所以你必须告诉他,第一个返回值的类型为ILogStuff
:
ILogStuff Logger = (_logMode) ? (ILogStuff)new LogToDisc() : new LogToConsole();
然后,第二个可能的值是正确的 - 在LogToConsole
类型和ILogStuff
接口之间存在隐式转换。
答案 3 :(得分:3)
表达式必须返回两种实现的通用类型。通过将实例显式地转换为接口,表达式将编译:
ILogStuff Logger = (_logMode) ?
(ILogStuff)new LogToDisc() :
(ILogStuff)new LogToConsole();
答案 4 :(得分:0)
Adil提供了确定此行为的部分,但我想解释为什么这种行为是明智的。
bool ? val1 : val2
这是一个表达方式。表达式需要具有在编译时可确定的类型。这使它更快,并且更快地发现错误。
现在,如果:
val是MyObject1
的一个实例,它扩展了SomeParent
并实现了MyInterface
,
和val2是MyObject2
的实例,它扩展SomeParent
并实现MyInterface
我们如何确定此表达式的编译时类型?我们可以尝试在MyObject1
和MyObject2
之间找到一种常见类型。什么是最明显的解决方案?你称之为SomeParent
还是MyInterface
?如果他们有2个接口,您会选择哪一个?
问题是这是一个烂摊子,并且需要一些非常人为的规则(实际上有更多的例子不太清楚),在一天结束时,它将不如当前定义那么直观。 / p>