关于运算符重载解析

3
假设有两个类,具有以下隐式和显式操作符模式:
class Foo
{
    public static implicit operator decimal (Foo foo)
    {
        throw new NotImplementedException();
    }

    public static implicit operator Foo (decimal value)
    {
        throw new NotImplementedException();
    }

    public static Foo operator +(Foo left, Foo right)
    {
        throw new NotImplementedException();
    }
}

class Bar
{
    public static explicit operator decimal (Bar bar)
    {
        throw new NotImplementedException();
    }

    public static explicit operator Foo(Bar bar)
    {
        throw new NotImplementedException();
    }
}

现在考虑以下代码:
var foo = new Foo();
var bar = new Bar();
var resultFooAddBar = foo + (decimal)bar;

隐式类型的 resutlFooAddBar 解析为 Foo,而加法操作符解析为 Foo Foo.operator +。为什么这段代码不会报错?该运算符也可以等效地解析为 decimal decimal.operator +。是因为用户定义的运算符总是被认为是更好的选择吗?即使如此,这个选择似乎有点奇怪,考虑到 Bar 有一个未使用的显式转换为 Foo 的方法,该方法将明确定义程序员想要使用的运算符:

var resultFooAddBar = foo + (Foo)bar; //好的,我明确表示我想要 Foo Foo.operator +

如果我们用第三个类 Tango(而不是 decimal),并定义了一个 Tango Tango.operator + (Tango, Tango) 和相同的隐式和显式运算符模式,则编译器会抛出一个模棱两可的调用错误。

为什么会区分用户定义的运算符和非用户定义的运算符?

更新:我创建了一个单独的程序集,包括以下类,以尝试 Servy 的解释:

namespace ExternalAssembly
{
    public class Tango
    {
        public static Tango operator +(Tango left, Tango right)
        {
            throw new NotImplementedException();
        }
    }
}

然后在FooBar中将decimal更改为Tango,并添加对ExternalAssembly dll的引用。在这种情况下,我仍然得到一个错误信息:"ConsoleApplication.Foo"和“ExternalAssembly.Tango”之间的'+ '运算符不明确。为什么编译器在这种情况下不会选择与我的原始问题中decimal相同的重载Foo Foo.operator +呢?请注意,保留了html标签。

@Servy decimal + decimal。从Foodecimal存在隐式转换,但编译器选择了从decimalFoo的隐式转换。请参见最后一段(刚刚编辑)。 - InBetween
1个回答

1
过载决策算法有一系列“优越性”指标,用于确定哪个方法/运算符的多个适用重载应该被使用。只有当这些指标都没有明确的“最佳”重载时,才会显示模棱两可的错误。
其中一个更好的度量标准是重载定义与调用点之间的“接近程度”。同一类中的定义比外部定义更“接近”,外部类中的定义比父类型之外的定义更接近,同一命名空间中的定义比外部命名空间中的定义更接近等等。您的定义比十进制数的+运算符更“接近”。(有关此主题的更多信息,请参见this article。)

好的,这正是我所预期的操作符解析行为。但仍然有一些妥协,以避免过于复杂。在这种情况下,明确地进行强制类型转换,而您选择不使用它,应该是编译器意识到可能没有选择“最佳”操作符的某种标志。无论如何,在我的国家有句话,“完美是善良的敌人”,所以它的工作方式可能是最好的。 - InBetween
如果我在一个不同的程序集和命名空间中创建了一个名为“Tango”的类,我仍然会得到一个模糊的调用。为什么这种情况会有任何不同于decimal呢? - InBetween

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接