Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler replaces explicit cast to my own type with explicit cast to .NET type?

I have the following code:

public struct Num<T>
{
    private readonly T _Value;

    public Num(T value)
    {
        _Value = value;
    }

    static public explicit operator Num<T>(T value)
    {
        return new Num<T>(value);
    }
}

...
double d = 2.5;
Num<byte> b = (Num<byte>)d;

This code compiles, and it surprises my. The explicit convert should only accept a byte, not a double. But the double is accepted somehow. When I place a breakpoint inside the convert, I see that value is already a byte with value 2. By casting from double to byte should be explicit.

If I decompile my EXE with ILSpy, I see the next code:

double d = 2.5;
Program.Num<byte> b = (byte)d;

My question is: Where is that extra cast to byte coming from? Why is that extra cast place there? Where did my cast to Num<byte> go to?

EDIT
The struct Num<T> is the entire struct, so no more hidden extra methods or operators.

EDIT
The IL, as requested:

IL_0000: nop
IL_0001: ldc.r8 2.5 // Load the double 2.5.
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: conv.u1 // Once again the explicit cast to byte.
IL_000d: call valuetype GeneriCalculator.Program/Num`1<!0> valuetype GeneriCalculator.Program/Num`1<uint8>::op_Explicit(!0) 
IL_0012: stloc.1
IL_0013: ret
like image 877
Martin Mulder Avatar asked May 07 '13 18:05

Martin Mulder


People also ask

What is cast type conversion in C++?

Explicit Type Conversion (Cast) Explicit conversion is required when the data cannot convert from one simple-type to another automatically by the compiler or when conversion may change the value by producing an incorrect result due to the possible loss of data (narrowing conversions).

Why can't I compile a cast statement?

If the cast can be interpreted in more than one way as static_cast followed by a const_cast, it cannot be compiled. In addition, C-style cast notation is allowed to cast from, to, and between pointers to incomplete class type.

What is the difference between implicit and explicit conversion in C++?

It is implicit conversion when a smaller data type is converted into a larger data type or derived class into a base class. On the other hand, the conversion in the opposite direction is known as explicit conversion.

Is this cast expression equivalent to the corresponding C-style cast expression?

This cast expression is exactly equivalent to the corresponding C-style cast expression. 3) If there are more than one expression or braced-init-list (since C++11) in parentheses, new_type must be a class with a suitably declared constructor.


1 Answers

Let's take a step back and ask some clarifying questions:

Is this program legal?

public struct Num<T>
{
    private readonly T _Value;

    public Num(T value)
    {
        _Value = value;
    }

    static public explicit operator Num<T>(T value)
    {
        return new Num<T>(value);
    }
}

class Program
{
    static void Main()
    {
        double d = 2.5;
        Num<byte> b = (Num<byte>)d;
    }
}

Yes.

Can you explain why the cast is legal?

As Ken Kin pointed out, I explain that here:

Chained user-defined explicit conversions in C#

Briefly: a user-defined explicit conversion may have a built-in explicit conversion inserted on "both ends". That is, we can insert an explicit conversion either from the source expression to the parameter type of the user-defined conversion method, or from the return type of the user-defined conversion method to the target type of the conversion. (Or, in some rare cases, both.)

In this case we insert a built-in explicit conversion to the parameter type, byte, so your program is the same as if you'd written:

        Num<byte> b = (Num<byte>)(byte)d;

This is desirable behaviour. A double may be explicitly converted to byte, so a double may also be explicitly converted to Num<byte>.

For a complete explanation, read section 6.4.5 "User-defined explicit conversions" in the C# 4 specification.

Why does the IL generated call op_Implicit instead of op_Explicit?

It doesn't; the question is predicated on a falsehood. The above program generates:

  IL_0000:  nop
  IL_0001:  ldc.r8     2.5
  IL_000a:  stloc.0
  IL_000b:  ldloc.0
  IL_000c:  conv.u1
  IL_000d:  call       valuetype Num`1<!0> valuetype Num`1<uint8>::op_Explicit(!0)
  IL_0012:  stloc.1
  IL_0013:  ret

You're looking at an old version of your program probably. Do a clean rebuild.

Are there other situations in which the C# compiler silently inserts an explicit conversion?

Yes; in fact this is the second time that question has come up today. See

C# type conversion inconsistent?

like image 144
Eric Lippert Avatar answered Oct 12 '22 14:10

Eric Lippert