为什么 C# 编译器不调用隐式强制转换运算符?

发布于 2024-07-10 11:12:27 字数 1514 浏览 10 评论 0原文

假设我们有以下类型:

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

Suppose we have following type:

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

And try to compile following code snippet:

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

int i = i1 + i2;

This snipped compiled well and without errors. i1 and i2 casts to integer and addition evaluated.

But if we have following type:

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

Try to compile another code snippet:

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;

And now the question, why compiler generate "error CS0019: Operator '+' cannot be applied to operands of type 'MyNullable<Money>' and 'MyNullable<Money>'"?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

青巷忧颜 2024-07-17 11:12:27

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

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

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

如果您只使用 Nullable ,它当然可以免费工作 - 而且您还可以获得编译器 + 运行时(装箱)支持。 是否有理由不在这里使用 Nullable

我会看一下规格; 在此期间,您可能会考虑将运算符提升为 MyNullable; 对于常规 Nullable,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(相同)“二元运算符重载解析”,预定义运算符确实得到了特别提及。 不过,我并不声称完全理解它。

That is an interesting question... it works with Decimal, for example, but not TimeSpan, which are both proper .NET types (unlike float etc that are primitives) and both have an + operator. Curious!

Of course, you can twist the arm with:

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

And it you just use Nullable<T> it'll work for free, of course - plus you get the compiler + runtime (boxing) support. Is there a reason not to use Nullable<T> here?

I'll look at the spec; in the interim, you might think about promoting the operator to the MyNullable<T>; with regular Nullable<T>, the C# compiler provides "lifted" operators for those supported by the type, but you can't do that yourself. The best you can do is offer all the obvious ones and hope the type supports it ;-p To access operators with generics, see here, available for free download here.

Note you'd probably want to apply the appropriate "lifted" checks - i.e.

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

Update

The different handling looks to relate to 14.7.4 (ECMA 334 v4) "Addition operator", where it is pre-defined for a range of types including decimal (so that was a bad test by me), since by 14.2.4 (same) "Binary operator overload resolution", the pre-defined operators do get special mention. I don't claim to understand it fully, though.

心房敞 2024-07-17 11:12:27

Marc 的观点是正确的 - 这是 C# 3.0 规范中的第 7.2.4 节 - 二元运算符重载解析。

基本上步骤是:

  • 我们需要解决“X + Y”的实现,其中 X 和 Y 都是 MyNullable
  • 查看第 7.2.5 节(候选用户定义运算符),我们最终得到一个空集,因为 MyNullable 不会重载 +。
  • 回到 7.2.4,候选运算符集是 + 的内置二元运算符集,即 int+int、decimal+decimal 等。
  • 然后应用 7.4.3 中的重载解析规则。 当我们执行 MyNullable时 + MyNullable 之所以有效,是因为每个参数都隐式转换为 int - 但是当我们执行 MyNullable时, + MyNullable不起作用,因为 Money + Money 不在候选运算符集中。

Marc is on the right lines - it's section 7.2.4 in the C# 3.0 spec - Binary Operator Overload Resolution.

Basically the steps are:

  • We need to resolve the implementation for "X + Y" where X and Y are both MyNullable<Money>.
  • Looking at section 7.2.5 (candidate user-defined operators) we end up with an empty set, as MyNullable<T> doesn't overload +.
  • Back in 7.2.4 the set of candidate operators is the built-in set of binary operators for +, i.e. int+int, decimal+decimal etc.
  • Overload resolution rules in 7.4.3 are then applied. When we're doing MyNullable<int> + MyNullable<int> this works because of the implicit conversions of each argument to int - but when we're doing MyNullable<Money> + MyNullable<Money> it doesn't work because Money + Money isn't in the set of candidate operators.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文