Страницы

Поиск по вопросам

пятница, 29 ноября 2019 г.

Почему компилятор C# неявно преобразует double в int при приведении к пользовательскому типу?

#c_sharp


Почему возможно явного преобразование типа double в тип foo, хотя в моем типе foo
определено только явное преобразование из типа int в тип foo?

Почему в моем случае тип double неявно преобразуется в тип int?

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

    


Ответы

Ответ 1



Такое поведение определено в спецификации языка в разделе 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 conversion. И в части пункта 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. ... Означает, что пользовательское явное преобразование может включать в себя три последовательных преобразований: Необязательное стандартное явное преобразование. (Исходный тип к типу операнда, в нашем случае это double в int) Пользовательское преобразование неявного или явного оператора. Необязательное стандартное явное преобразование. (Тип результата из 2 пункта к конечному типу. В нашем случае отстуствует этот пункт). Т.е. можно сказать, что происходит примерно так: // Пользовательское явное преобразование. Foo foo = (Foo)doub; // Будет вот так. Foo foo = (Foo)(int)doub; Если глянуть в IL, то увидим там опкод conv.i4 перед вызовом пользовательского приведения. IL_0000: ldc.r8 15.7 IL_0009: dup IL_000a: conv.i4 IL_000b: call valuetype Foo Foo::op_Explicit(int32) Т.е. все работает в соответствии со спецификацией.

Ответ 2



Если определить перегруженный вариант оператора приведения для нужного типа, то выбирается правильный вариант, и приведение выполняется как положено. Если для нужного типа преобразование не определено, но определен хотябы один оператор приведения, то преобразование происходит молча из любого числового типа в любой числовой тип. Важно, это наблюдается только с числовыми типами. char и другие встроенные типы, семантика которых не подразумевает число, как положено возвращают ошибку на этапе компиляции. Чуть раньше ответили что это особенность, не очень приятно но факт. Будьте внимательны, таким способом вполне можно выстрелить себе в ногу. В IL это выглядит так: 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

Комментариев нет:

Отправить комментарий