【问题标题】:Why C# compiler doesn't call implicit cast operator?为什么 C# 编译器不调用隐式强制转换运算符?
【发布时间】:2008-12-26 11:32:20
【问题描述】:

假设我们有以下类型:

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);
    }
}

并尝试编译以下代码sn-p:

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);
    }
}

尝试编译另一个代码sn-p:

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' 和 'MyNullable' 类型的操作数”? p>

【问题讨论】:

    标签: c# casting operator-overloading compiler-errors


    【解决方案1】:

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

    当然,你可以用以下方式扭动手臂:

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

    如果您只需使用Nullable&lt;T&gt;,它当然可以免费工作——另外您还可以获得编译器+运行时(装箱)支持。这里有不使用Nullable&lt;T&gt;的理由吗?

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

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

    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(相同)“二元运算符重载决议”,预定义的运算符确实得到特别提及。不过,我并没有声称完全理解它。

    【讨论】:

    • 不幸的是, Nullable 也不起作用,在我看来,内置原语以某种方式得到特殊处理,尽管我不知道为什么。我在 C# 规范中查找了用户定义的隐式转换,但恐怕我迷失在那里的技术杂念中......希望你能找到它,奇怪..
    • @lassevk - Nullable 工作正常 - 只是 m1 + m2 的结果是 Nullable,而不是 Money
    • MyNullable 正在做相反的事情。 Nullable 类型的值不能隐式使用。 MyNullable 正试图实现这一目标。
    • 即“钱?m3 = m1 + m2;” - 编译并运行,结果 30 符合预期
    • @AnthonyWJones - 并且有充分的理由 - 如果隐式转换没有值应该返回什么?显式强制转换(.Value)爆炸(合理)。否则,有 GetValueOrDefault()。
    【解决方案2】:

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

    基本上步骤是:

    • 我们需要解决“X + Y”的实现,其中 X 和 Y 都是 MyNullable&lt;Money&gt;
    • 查看第 7.2.5 节(候选用户定义的运算符),我们最终得到一个空集,因为 MyNullable&lt;T&gt; 不会重载 +。
    • 在 7.2.4 中,候选运算符集是内置的 + 二进制运算符集,即 int+int、decimal+decimal 等。
    • 然后应用 7.4.3 中的重载解决规则。当我们在做MyNullable&lt;int&gt; + MyNullable&lt;int&gt; 时,这是可行的,因为每个参数都隐式转换为int - 但是当我们在做MyNullable&lt;Money&gt; + MyNullable&lt;Money&gt; 时,它不起作用,因为Money + Money 不是' t 在候选运算符集中。

    【讨论】:

      猜你喜欢
      • 2023-03-13
      • 1970-01-01
      • 2016-06-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-23
      • 1970-01-01
      • 1970-01-01
      • 2020-09-30
      相关资源
      最近更新 更多