6

假设我们有以下类型:

struct MyNullable<T> where T : struct
{
    T Value;

    public bool HasValue;

    public MyNullable(T value)
    {
        this.Value = value;
        this.HasValue = true;
    }

    public static implicit operator T(MyNullable<T> value)
    {
        return value.HasValue ? value.Value : default(T);
    }
}

并尝试编译以下代码片段:

MyNullable<int> i1 = new MyNullable<int>(1);
MyNullable<int> i2 = new MyNullable<int>(2);

int i = i1 + i2;

这剪辑编译得很好,没有错误。i1 和 i2 转换为整数并评估加法。

但是如果我们有以下类型:

struct Money
{
    double Amount;
    CurrencyCodes Currency; /*enum CurrencyCode { ... } */

    public Money(double amount, CurrencyCodes currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public static Money operator + (Money x, Money y)
    {
        if (x.Currency != y.Currency)
            // Suppose we implemented method ConvertTo
            y = y.ConvertTo(x.Currency); 

        return new Money(x.Amount + y.Amount, x.Currency);
    }
}

尝试编译另一个代码片段:

MyNullable<Money> m1 = 
   new MyNullable<Money>(new Money(10, CurrenciesCode.USD));
MyNullable<Money> m2 = 
   new MyNullable<Money>(new Money(20, CurrenciesCode.USD));

Money m3 = m1 + m2;

现在的问题是,为什么编译器会生成“错误 CS0019:运算符 '+' 不能应用于 'MyNullable<Money>' 和 'MyNullable<Money>' 类型的操作数”?

4

2 回答 2

10

这是一个有趣的问题......它适用于Decimal,例如,但不是TimeSpan,它们都是正确的 .NET 类型(不像float等是原语)并且都有一个 + 运算符。好奇的!

当然,您可以使用以下方法扭动手臂:

Money m3 = (Money)m1 + (Money)m2;

当然,您只需使用Nullable<T>它就可以免费工作 - 再加上您可以获得编译器 + 运行时(装箱)支持。有理由不在这里使用Nullable<T>吗?

我会看看规格;在此期间,您可能会考虑将运算符提升到MyNullable<T>; 使用常规Nullable<T>,C# 编译器为该类型支持的那些提供“提升”运算符,但您不能自己这样做。你能做的最好的就是提供所有明显的,并希望类型支持它;-p 要使用泛型访问运算符,请参见此处,可在此处免费下载。

请注意,您可能希望应用适当的“提升”检查 - 即

x + y => (x.HasValue && y.HasValue)
          ? new MyNullable<T>(x.Value + y.Value)
          : new MyNullable<T>();

更新

不同的处理看起来与 14.7.4(ECMA 334 v4)“加法运算符”有关,其中它是为包括十进制在内的一系列类型预定义的(所以这对我来说是一个糟糕的测试),因为从 14.2.4(同样)“二元运算符重载决议”,预定义的运算符确实得到特别提及。不过,我并不声称完全理解它。

于 2008-12-26T11:57:57.807 回答
9

Marc 在正确的路线上 - 它是 C# 3.0 规范中的第 7.2.4 节 - 二进制运算符重载解决方案。

基本上步骤是:

  • 我们需要解决“X + Y”的实现,其中 X 和 Y 都是MyNullable<Money>
  • 查看第 7.2.5 节(候选用户定义的运算符),我们最终得到一个空集,因为MyNullable<T>不会重载 +。
  • 在 7.2.4 中,候选运算符集是内置的二元运算符集,用于 +,即 int+int、decimal+decimal 等。
  • 然后应用7.4.3 中的重载解决规则。当我们这样做时MyNullable<int> + MyNullable<int>,由于每个参数的隐式转换为int- 但当我们这样做MyNullable<Money> + MyNullable<Money>时它不起作用,因为Money + Money不在候选运算符集中。
于 2008-12-27T12:16:54.947 回答