当使用的值可以隐式转换为这些结构时,为什么我不能使用采用两个结构的重载 ==?

Why can't I use the overloaded == that takes two structs when the values used are implicitly convertable to these structs?

我注意到在应用 operator== 时,用户定义的隐式转换为 int 和用户定义的隐式转换为任意结构 MyStruct 的行为之间存在差异。

如果我有:

public struct IntA
{
    public IntA(int value)
    { m_value = value; }

    public static implicit operator int(IntA a)
    { return a.m_value; }

    private int m_value;
}

public struct IntB
{
    public IntB(int value)
    { m_value = value; }

    public static implicit operator int(IntB b)
    { return b.m_value; }

    private int m_value;
}

然后编译以下代码:

{
    var a = new IntA(3);
    var b = new IntB(4);

    bool equal = (a == b); // ok! converted to int and used int operator==

    // ...
}

这使用我的用户定义 implicit operator int 用于 IntAIntB 转换为 int,然后调用 operator==(int, int).

但是,如果我有:

public struct MyStruct
{
    public MyStruct(int value)
    { m_value = value; }

    public static bool operator==(MyStruct lhs, MyStruct rhs)
    { return lhs.m_value == rhs.m_value; }

    public static bool operator!=(MyStruct lhs, MyStruct rhs)
    { return lhs.m_value != rhs.m_value; }

    private int m_value;
}

public struct MyStructA
{
    public MyStructA(int value)
    { m_value = new MyStruct(value); }

    public static implicit operator MyStruct(MyStructA a)
    { return a.m_value; }

    private MyStruct m_value;
}

public struct MyStructB
{
    public MyStructB(int value)
    { m_value = new MyStruct(value); }

    public static implicit operator MyStruct(MyStructB b)
    { return b.m_value; }

    private MyStruct m_value;
}

那么下面的代码编译:

{
    var a = new MyStructA(3);
    var b = new MyStructB(4);

    bool equal = (a == b); // compile error: Operator `==' cannot be applied to operands of type `MyStructA' and `MyStructB'
                           // why can't it convert to MyStruct and use that operator==?

    // ...
}

我希望它和前面的例子一样,并使用我的用户定义 implicit operator MyStruct 转换为 MyStruct,然后调用 operator==(MyStruct, MyStruct).

它不会那样做。为什么不?从编译器的角度来看,这两种情况有何不同?

答案在语言规范中。重点是我的。

C# Language Specification 7.3.4

An operation of the form x op y, where op is an overloadable binary operator, x is an expression of type X, and y is an expression of type Y, is processed as follows:

  • The set of candidate user-defined operators provided by X and Y for the operation operator op(x, y) is determined. The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y, each determined using the rules of §7.3.5. If X and Y are the same type, or if X and Y are derived from a common base type, then shared candidate operators only occur in the combined set once.
  • If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary operator op implementations, including their lifted forms, become the set of candidate operators
    for the operation
    . The predefined implementations of a given operator are specified in the description of the operator (§7.8 through §7.12). For predefined enum and delegate operators, the only operators considered are those defined by an enum or delegate type that is the binding-time type of one of the operands.
  • The overload resolution rules of §7.5.3 are applied to the set of candidate operators to select the best operator with respect to the argument list (x, y), and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, a binding-time error occurs.

因此,如果没有初始匹配,它会将所有内部定义的 == 运算符视为候选。由于 int 有一个,但 MyStruct 没有,您会看到不同的行为。