Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the C# compiler handle overloading explicit cast operators?

The compiler should translate this code:

public static explicit operator Int64(MyNumber n)
{
    return n.ToInteger();
}

public static explicit operator Double(MyNumber n)
{
    return n.ToDouble();
}

to two methods that have the same name and signature but differ only by their return type, e.g.

public static Int64 ExplicitCast(MyNumber n)
...

public static Double ExplicitCast(MyNumber n)
...

However, we are not allowed to have methods that differ only by their return type. What happens behind the curtains?

like image 560
Alex Sk Avatar asked Mar 08 '16 12:03

Alex Sk


2 Answers

Technically the CLS (the Common Language Specification, a specification that specify a subsect of the .NET virtual machine that all .NET languages should support) says that the name of the explicit cast method should be op_Explicit (see for example http://goo.gl/wn8dHq).

The limitation that you can't have multiple methods with the same name and only different return types is a limitation of C#. The IL language (that is the language of the .NET virtual machine) doesn't have this limitation.

See for example: https://stackoverflow.com/a/442100/613130

Some languages (such as MSIL), however, do allow overloading by return type. They too face the above difficulty of course, but they have workarounds, for which you'll have to consult their documentation.

and https://blogs.msdn.microsoft.com/abhinaba/2005/10/07/c-cil-supports-overloading-by-return-type/

However, CIL does support overloading methods by return types, even though C#, VB does not . To implement convertion operator overloading C# compiler uses this feature (I know of one usage and I’m sure that there are more :) )

(that is exactly the case asked here)

If you want to look at the ECMA-335 standard:

I.8.11.1 Method definitions

The method signature defines the calling convention, type of the parameters to the method, and the return type of the method

If you are interested to know how the method can be called... Well... Clearly if the IL language supports overload by return type, then its call instruction must support it :-)

For example http://goo.gl/CS4FPb:

call int64 MyNumber::op_Explicit(class MyNumber)

vs

call float64 MyNumber::op_Explicit(class MyNumber)

Note that CLS normally forbids overloading only based on return type... But it has an exception for op_Implicit (the implicit cast operator) and op_Explicit (the explicit cast operator) (from the same ECMA-335 file):

CLS Rule 38: Properties and methods can be overloaded based only on the number and types of their parameters, except the conversion operators named op_Implicit and op_Explicit, which can also be overloaded based on their return type.

like image 93
xanatos Avatar answered Sep 24 '22 01:09

xanatos


I'm not sure the purpose of your question, so this is the best answer I can give you. The above code compiles to something like this

MyNumber.op_Explicit:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  callvirt    UserQuery+MyNumber.ToInteger
IL_0007:  stloc.0     
IL_0008:  br.s        IL_000A
IL_000A:  ldloc.0     
IL_000B:  ret         

MyNumber.op_Explicit:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  callvirt    UserQuery+MyNumber.ToDouble
IL_0007:  stloc.0     
IL_0008:  br.s        IL_000A
IL_000A:  ldloc.0     
IL_000B:  ret      

The reason it appears "inconsistent" is because of the keywords explicit operator. It tells the compiler to generate code differently from the way it does in an explicit cast.

You'd get something like this without it:

MyNumber.Double2:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  callvirt    UserQuery+MyNumber.ToDouble
IL_0007:  stloc.0     
IL_0008:  br.s        IL_000A
IL_000A:  ldloc.0     
IL_000B:  ret    
like image 41
Kir Avatar answered Sep 26 '22 01:09

Kir