Reputation: 3056
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
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
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