0

我有一个奇怪的行为,我想了解它。但是我在网上没有找到好的答案:(

这是情况,我已经抽象了名称和逻辑以专注于该问题。有 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();

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

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

错误 :

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

您对这种行为有任何想法吗?

在此先感谢您的时间。

最好的问候,并保持它没有错误!

4

4 回答 4

3

您对这种行为有任何想法吗?

绝对地。条件运算符表达式的类型必须是第二个操作数的类型或第三个操作数的类型。(如果这两种类型不相同,则其中一种类型必须隐式转换为另一种类型。)编译器不会尝试查找“下公分母”类型,结果的使用是' 也不重要(编译器不会“注意到”您将结果分配给 type 的变量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 节。

于 2013-09-27T13:49:20.897 回答
1

错误很明显

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

A您已经定义了andBAand之间的隐式转换CB但是和之间没有隐式转换C

条件运算符要求:

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

于 2013-09-27T13:49:55.847 回答
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)和更少的内存使用。

于 2013-09-27T14:32:58.607 回答
0

您可以看看?: Operator (C# Reference)中的注释 它指出

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

于 2013-09-27T13:50:55.757 回答