Alex78191
Alex78191

Reputation: 3056

Why does the compiler implicitly convert a double to int when an explicit user-defined operator exists from int to Foo?

Why is an explicit conversion from double to Foo possible, even though Foo only defined an explicit conversion from int to Foo?

Why is in my case double implicitly converted to an int?

using System;

class Program
{
    static void Main(string[] args)
    {
        double doub = 15.7;
        Foo foo = (Foo)doub;
        Console.WriteLine(foo.value); //writes "15"
    }
}

struct Foo
{
    public int value;
    public static explicit operator Foo(int val) //no matter if implicit
    {
        return new Foo { value = val };
    }
}

Upvotes: 5

Views: 558

Answers (2)

J...
J...

Reputation: 31403

The relevant section of the C# specification is

The section is lengthy, but it distills to this : you are defining an explicit conversion with (Foo)doub, and since you are using an explicit conversion the compiler will choose the most specific available conversions, including conversions that are explicit-only, to make the intermediate steps along the conversion path (if they are available).

If U contains exactly one user-defined conversion operator that converts from SX to TX, then this is the most specific conversion operator. If no such operator exists, or if more than one such operator exists, then the conversion is ambiguous and a compile-time error occurs. Otherwise, the user-defined conversion is applied:

If S is not SX, then a standard explicit conversion from S to SX is performed.
The most specific user-defined conversion operator is invoked to convert from SX to TX.
If TX is not T, then a standard explicit conversion from TX to T is performed.

(emphasis mine)

Here S is the source type and T is the destination type, while SX is the most specific source type defined in the explicit operators of the destination type (ie; Foo) and TX is the most specific destination type defined in the explicit operators of T.

Here an explicit conversion exists from double to int and it is therefore chosen as the most appropriate to convert from the source argument type (double -- S ) to the input argument type of the explicit operator (int -- SX). It does not need to be explicit since you have already been explicit about making the conversion to Foo.

This only works because there are no ambiguous alternatives. If you defined Foo to have :

struct Foo
{
    public int value;
    public uint uvalue;
    public static explicit operator Foo(int val)
    {
        return new Foo { value = val };
    }


    public static explicit operator Foo(uint val)
    {
        return new Foo { uvalue = val };
    }
}

for example, this produces a compile-time error (with the same Main) of :

Error 1 Ambiguous user defined conversions 'Foo.explicit operator Foo(uint)' and 'Foo.explicit operator Foo(int)' when converting from 'double' to 'Foo'

Again, this is by the book (per the first paragraph quoted above) since the set of available conversions U now contains two equally valid explicit conversions and the compiler cannot decide whether you intend to invoke the int conversion or the uint conversion. In the inital example there is only once choice, and it is clear, so the compiler takes it.

Upvotes: 1

Jeroen Vannevel
Jeroen Vannevel

Reputation: 44439

I believe this is layed out in section 6.4.3 of the C# 5 Language Specification.

A first hint is given in section 6.2.8 User Defined explicit conversions (emphasis mine):

A user-defined explicit conversion consists of an optional standard explicit conversion, followed by execution of a user-defined implicit or explicit conversion operator, followed by another optional standard explicit conversion

Notice how not one but potentially three conversions occur.

Now, to know what is a 'standard explicit conversion' we have to look at section 6.2.3 Standard explicit conversions:

The standard explicit conversions are all standard implicit conversions plus the subset of the explicit conversions for which an opposite standard implicit conversion exists. In other words, if a standard implicit conversion exists from a type A to a type B, then a standard explicit conversion exists from type A to type B and from type B to type A.

And looking back at section 6.3.1 Standard implicit conversions we can see that this is a part of it:

  • Implicit numeric conversions (§6.1.2)

In other words: an explicit numeric conversion (like double -> int) can be applied before the user-defined conversion.

If we now look at 6.4.3 Evaluation of user-defined conversions we see the following (emphasis mine):

First, if required, performing a standard conversion from the source type to the operand type of the userdefined or lifted conversion operator

Next, invoking the user-defined or lifted conversion operator to perform the conversion.

Finally, if required, performing a standard conversion from the result type of the user-defined or lifted conversion operator to the target type.

Which is exactly what happens in your scenario.

Upvotes: 8

Related Questions