如果使用内联隐式转换中的奇怪行为

时间:2013-09-27 13:46:08

标签: c# .net clr

我有一种奇怪的行为,我想理解它。但我在网上找不到好的答案:(

在这种情况下,我已经抽象出了专注于这个问题的名称和逻辑。有3种类型,A,B和C. B& C将隐式运算符定义为转换为A对象。

public class A
{
    public static implicit operator A(B input){ /* Convert B to A */ }
    public static implicit operator A(C input) { /* Convert C to A*/ }
}

public class B { }
public class C { }

然后,当我这样做时,代码编译并正常工作:

A myObject = null;
if (condition)
    myObject = new B();
else
    myObject = new C();

但是当我使用内联编写相同的逻辑时,我收到了一个错误:

A myObject = condition ? new B() : new C();

错误:

Type of conditional expression cannot be determined because there is no implicit conversion between 'B' and 'C'

你对这种行为有什么看法吗?

提前感谢您的时间。

最好的问候,并保持无bug!

4 个答案:

答案 0 :(得分:3)

  

你对这种行为有什么看法吗?

绝对。条件运算符表达式的类型必须是第二个操作数的类型或第三个操作数的类型。 (如果这两种类型不相同,则其中一种类型必须可以隐式转换为另一种类型。)编译器不会尝试查找“较低公分母”类型,而使用<结果的/ em>也不重要(编译器没有“注意到”你将结果赋值给A类型的变量。)

您可以通过显式转换操作数来自行解决此问题:

A myObject = condition ? (A) new B() : new C();

A myObject = condition ? new B() : (A) new C();

请注意,这不仅限于用户定义的转换运算符;基于派生类的简单引用转换也是如此:

Button x = new Button();
String y = "foo";
object z = condition ? x : y; // Compile-time error

有关详细信息,请参阅C#规范的第7.14节。

答案 1 :(得分:1)

错误很明显

  

'B'和'C'之间没有隐式转换

您已定义AB以及AC之间的隐式转化。但BC之间没有隐式转换。

Conditional operator要求:

  

first_expression和second_expression的类型必须是   相同,或者必须存在从一种类型到另一种类型的隐式转换。

答案 2 :(得分:0)

您可以查看?: Operator (C# Reference)中的评论  它声明

  

first_expression和second_expression的类型必须相同,或者从一种类型到另一种类型必须存在隐式转换。

答案 3 :(得分:0)

@Jon Skeet: 看了一下生成的MSIL后,这就是我发现的内容,它真的很有趣。这是我写的两种测试方法:

static void ClassicalIf(bool condition)
{
    int i = 0;
    if (condition)
        i = 1;
    else
        i = 2;
}

static void InlineIf(bool condition)
{
    int i = condition ? 1 : 2;
}

这是带有注释的MSIL,因此任何人都可以理解已完成的内容以及为什么内联语法确实需要隐式转换。

对于内联if:

.method private hidebysig static void InlineIf(bool condition) cil managed
{
    .maxstack 1
    .locals init (
        [0] int32 i)
    L_0000: nop 
    L_0001: ldarg.0         -- Load argument '0' onto the stack
    L_0002: brtrue.s L_0007 -- Branch to L_0007 if value is non-zero
    L_0004: ldc.i4.2        -- Push 2 onto the stack
    L_0005: br.s L_0008     -- Branch to L_0008
    L_0007: ldc.i4.1        -- Push 1 onto the stack
    L_0008: nop 
    L_0009: stloc.0         -- Pop from stack into local variable 0
    L_000a: ret 
}

如果符合以下情况,那么这就是“正常”的那个:

.method private hidebysig static void ClassicalIf(bool condition) cil managed
{
    .maxstack 2
    .locals init (
        [0] int32 i,
        [1] bool CS$4$0000) -- Additional bool for if
    L_0000: nop 
    L_0001: ldc.i4.0        -- Push 0 onto the stack
    L_0002: stloc.0         -- Pop from stack into local variable '0'
    L_0003: ldarg.0         -- Load argument '0' onto the stack
    L_0004: ldc.i4.0        -- Push 0 onto the stack 
    L_0005: ceq             -- Push 1 if value1 equals value2 (on stack), else push 0.
    L_0007: stloc.1         -- Pop from stack into local variable '1'
    L_0008: ldloc.1         -- Load local variable '1' onto stack.
    L_0009: brtrue.s L_000f -- Branch to L_000f if value is non-zero
    L_000b: ldc.i4.1        -- Push 1 onto the stack 
    L_000c: stloc.0         -- Pop from stack into local variable '0'
    L_000d: br.s L_0011     -- Branch to L_0011
    L_000f: ldc.i4.2        -- Push 2 onto the stack 
    L_0010: stloc.0         -- Pop from stack into local variable '0'
    L_0011: ret 
}

所以我也会尝试尽可能使用内联。较少的指令(因此CPU)和较少的内存使用。