Why does the compiler implicitly convert double to int when an explicit user statement exists from int to Foo?

Why is there an explicit conversion from double to Foo , although Foo only defines an explicit conversion from int to Foo ?

Why in my case double implicitly converted to 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 }; } } 
+5
source share
2 answers

I believe this is outlined in Section 6.4.3 of the C # 5 Language Specifications.

The first hint is given in section 6.2.8. Explicit user-defined conversions. (my selection):

A custom explicit conversion consists of an optional standard explicit conversion , followed by a custom implicit or explicit conversion operator , followed by an optional standard explicit conversion

Note that one, but potentially three, conversions do not occur.

Now, to find out what the “standard explicit conversions” are, we need to look at section 6.2.3 Standard explicit conversions :

Standard explicit conversions are all standard implicit conversions plus a subset of explicit conversions for which the opposite standard implicit conversion exists. In other words, if a standard implicit conversion exists from type A to 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 see that this is part of it:

  • Implicit numerical conversions (§6.1.2)

In other words: an explicit numerical conversion (e.g. double -> int ) can be applied before the user conversion.

If we look at 6.4.3 Evaluation of user-defined conversions , we will see the following (my selection):

First, if required, performing a standard conversion from the source type to the operand type of the operator set by the user or the canceled conversion

Then call the custom or canceled conversion operator to perform the conversion.

Finally, if required, perform a standard conversion from the type of result defined by the user or the removed conversion operator to the target type.

What exactly is happening in your scenario.

+8
source

The relevant section of the C # specification is -

The section is long, but it prefers this: you define the explicit conversion using (Foo)doub , and since you use the explicit conversion, the compiler will select the most accessible conversions available, including conversions that are explicit, to take intermediate steps along the conversion path (if available).

If U contains exactly one custom conversion operator that converts from SX to TX, then this is the most specific conversion operator. If such an operator does not exist, or if more than one such operator exists, then the conversion is ambiguous and a compile time error occurs. Otherwise, a custom transformation is applied:

If S is not SX, then the standard explicit conversion from S to SX is performed.
To convert from SX to TX, the most specific user conversion operator is called.
If TX is not T, a standard explicit conversion from TX to T is performed.

(my emphasis)

Here S is the source type and T is the destination type, and SX is the most specific source type defined in explicit destination type operators (i.e. Foo ) and TX is the most specific destination type defined in explicit T operators.

Here, an explicit conversion exists from double to int , and therefore it is selected as the most suitable for conversion from the type of the original argument ( double -- S ) to the type of the input argument of the explicit operator ( int -- SX ). It does not have to be explicit, since you have already explicitly pointed to 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 leads to a compile-time error (with the same Main ):

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

Again, this is from the book (in the first paragraph above), since the set of available conversions U now contains two equally valid explicit conversions, and the compiler cannot decide whether you intend to use the int transform or the uint transform. In the inital example, there is only one choice, and this is understandable, so the compiler takes it.

+1
source

Source: https://habr.com/ru/post/1244071/


All Articles