Why does the C# compiler implicitly convert double to int when casting to a user-defined type?


Why is it possible to explicitly convert from double to foo when my type foo only has an explicit conversion from int to foo?

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

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


Такое поведение определено в спецификации языка в разделе M. Conversions (M. Преобразования).
Пункт M.2.8 User-defined explicit conversions

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. The exact rules for evaluating user-defined
explicit conversions are described in §User-defined explicit

И в части пункта
M.4.3 Evaluation of user-defined conversions
про это тоже говорится:

… Once a most specific user-defined conversion operator has been
identified, the actual execution of the user-defined conversion
involves up to three steps: First, if required, performing a standard
conversion from the source type to the operand type of the
user-defined 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. …

Означает, что пользовательское явное преобразование может включать в себя три последовательных преобразований:

  1. Необязательное стандартное явное преобразование. (Исходный тип к типу операнда, в нашем случае это double в int)
  2. Пользовательское преобразование неявного или явного оператора.
  3. Необязательное стандартное явное преобразование. (Тип результата из 2 пункта к конечному типу. В нашем случае отстуствует этот пункт).

Т.е. можно сказать, что происходит примерно так:

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

If you look in the IL, you will see the conv.i4 opcode there before calling the custom 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.

Scroll to Top