隐式运算符和编译器错误

Implicit operators and a compiler error

我有一个 class(为了这个问题的目的而简化),它包装了一个 decimal 值并使用了几个 implicit operator 声明在类型和包装类型之间进行转换值:

private class DecimalWrapper
{
    public decimal Value { get; private set; }

    public DecimalWrapper(decimal value)
    {
        Value = value;
    }

    public static implicit operator decimal(DecimalWrapper a)
    {
        return a != null ? a.Value : default(decimal);
    }

    public static implicit operator DecimalWrapper(decimal value)
    {
        return new DecimalWrapper(value);
    }
}

这里 implicit operator 的用法允许完成这样的事情:

DecimalWrapper d1 = 5; // uses implicit operator DecimalWrapper
DecimalWrapper d2 = 10;
var result = d1 * d2; // uses implicit operator decimal

Assert.IsTrue(result.Equals(50));
Assert.IsTrue(result == 50);

现在,考虑第二个 class(再次简化),它有一个重载的构造函数,可以采用 decimalDecimalWrapper:

private class Total
{
    private readonly DecimalWrapper _total;

    public Total(DecimalWrapper total)
    {
        _total = total;
    }

    public Total(decimal totalValue)
    {
        _total = totalValue;
    }
}

我希望能够通过传入一个整数值来实例化 Total 的实例,该整数值将转换为小数:

var total = new Total(5);

但是,这会导致编译器错误:

The call is ambiguous between the following methods or properties: 'Namespace.Total.Total(TypeTests.DecimalWrapper)' and 'Namespace.Total.Total(decimal)'

要解决此问题,您必须删除 implicit operator decimal 或指定值 5 实际上是 decimal:

var total = new Total(5m);

一切都很好,但我不明白为什么 implicit operator decimal 与此相关。那么,这是怎么回事?

您要查找语言规范中的引文吗?

这与重载决议有关。当您将 int 值指定为构造函数参数时,不会考虑重载 "best",因为两者都需要转换。规范没有考虑两级转换与一级不同,所以两个构造函数重载是等价的。

作为 ,您可以通过删除其中一个构造函数轻松解决问题。他建议删除 DecimalWrapper 重载,但由于您的字段属于 DecimalWrapper 类型,所以我会改用 decimal 重载。这样做,如果您为构造函数指定一个 int 值,编译器将隐式转换为 decimal,然后为您转换为 DecimalWrapper。如果您为构造函数指定一个 decimal 值,编译器将为该调用隐式转换为 DecimalWrapper,这是您的 decimal 构造函数无论如何都会做的。

当然,解决该问题的另一种方法是将其他构造函数添加到 Total class,例如需要 int 的一个。然后不需要转换,将选择 int 构造函数。但这对我来说似乎有点矫枉过正。