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
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).
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.
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.
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.
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 ofop_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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With