Why is it possible to explicitly convert the type double to the type foo, although in my type foo only an explicit conversion from type int to type foo is defined?

Why in my case the double type is implicitly converted to the int type?

using System; class Program { static void Main(string[] args) { double doub = 15.7; Foo foo = (Foo)doub; Console.WriteLine(foo.value); //выводит 15 } } struct Foo { public int value; public static explicit operator Foo(int val) { return new Foo { value = val }; } } 

    2 answers 2

    This behavior is defined in the language specification in the M. Conversions section. Item M.2.8 User-defined explicit conversions

    A standardized explicit conversion was followed by a standardized explicit conversion. The exact rules for evaluating user-defined explicit converters are described in §User-defined explicit conversion.

    And in part of paragraph M.4.3 Evaluation of user-defined conversions about this, too, it says:

    ... Once there is a need, it’s not user-defined or lifted conversion operator. Next, invoking the user-defined or lifted conversion operator to perform the conversion. Finally, if required, it can be used for the target type. ...

    Indicates that a user-defined explicit conversion may include three consecutive conversions:

    1. Optional standard explicit conversion. (The initial type to the type of the operand, in our case it is double to int )
    2. Custom conversion implicit or explicit operator.
    3. Optional standard explicit conversion. (The type of the result is from 2 points to the final type. In our case this item is missing).

    Those. You can say what happens like this:

     // Пользовательское явное преобразование. Foo foo = (Foo)doub; // Будет вот так. Foo foo = (Foo)(int)doub; 

    If you look at IL, you will see the conv.i4 opcode there before calling the user cast.

     IL_0000: ldc.r8 15.7 IL_0009: dup IL_000a: conv.i4 IL_000b: call valuetype Foo Foo::op_Explicit(int32) 

    Those. everything works according to the specification.

    • well, an unpleasant feature .... - rdorn
    • I remember, I found something similar to Eric Lippert, it seemed to describe why they did that. - skubarenko
    • the reasons may have been, but worse than this feature, only the type dynamic , violating the strict typing of the language. Although, too, in general, it is clear why they did it - rdorn
    • it works by the way with dynamic too, only IL looks awful =) - rdorn
    • @rdorn, idiotic feature, I would even say. Stupidly copied the coercion rule from C ++, only here in C ++ the double to int has implicit conversion, unlike C # - ixSci
    1. If you determine the overloaded version of the cast operator for the desired type, then the correct one is selected, and the cast is performed as expected.
    2. If no conversion is defined for the required type, but at least one reduction operator is defined, then the conversion occurs silently from any numeric type to any numeric type.

    Important, it is observed only with numeric types. char and other built-in types, the semantics of which does not imply a number, as expected return an error at the compilation stage.

    A little earlier they said that this is a feature, not very nice but true. Be careful, in this way it is quite possible to shoot yourself in the foot.

    In IL, it looks like this:

     IL_0001: ldc.r8 66 66 66 66 66 66 2F 40 //doub = 15.7 IL_000A: stloc.0 // doub IL_000B: ldloc.0 // doub IL_000C: conv.i4 //преобразование в Int32 IL_000D: call UserQuery+Foo.op_Explicit//преобразование в Foo 
    • Eh, did not notice and also wrote about IL :) - skubarenko