Wednesday, October 13, 2010

Using C#'s implicit type conversions from other .NET languages

One interesting C# feature is the ability to define a method that implements implicit conversion from one type to another. In this post I'm going to show how to use this feature from IronPython, F#, VB.NET and IronRuby.

Example



In order to illustrate the implicit conversion feature we're going to use the following classes:


namespace Langexplr.Experiments
{
public class Complex
{
public double Real { get; set; }
public double Img { get; set; }

public static implicit operator Complex(double real)
{
return new Complex() { Real = real };
}

public static implicit operator Polar(Complex complex)
{
return new Polar() { Angle = Math.Atan(complex.Img/complex.Real),
Length = Math.Sqrt(complex.Real*complex.Real +
complex.Img*complex.Img) };
}

public static implicit operator double(Complex complex)
{
return Math.Sqrt(complex.Real*complex.Real +
complex.Img*complex.Img);
}

}

public class Polar
{
public double Angle { get; set; }
public double Length { get; set; }
}
}



The Complex class is a simple definition of a complex number. The Polar class is defined(conveniently) to represent a complex number in polar form. The Complex class defines three implicit conversions:

  1. From double to a complex number

  2. From Complex to Polar

  3. From Complex to double


The following C# code shows a use of this feature:


using Langexplr.Experiments;
using System;

class main
{
public static void Main(string[] args)
{
Complex c = 10.3;
Polar p = new Complex() {Real = 12.3, Img = 5.2};
double abs = c;

Console.WriteLine("abs:{0} Polar: {1},{2}", abs ,p.Angle ,p.Length);
}
}


By looking at the definitions generated by the compiler for the Complex class, we can see several definitions for the op_Implicit method with different parameters and return types.

...
.method public hidebysig specialname static
class Langexplr.Experiments.Complex
op_Implicit(float64 real) cil managed
...
.method public hidebysig specialname static
class Langexplr.Experiments.Polar
op_Implicit(class Langexplr.Experiments.Complex complex) cil managed
...
.method public hidebysig specialname static
float64 op_Implicit(class Langexplr.Experiments.Complex complex) cil managed
...



Now these uses of the Complex class will be presented on different .NET languages.

IronPython



As described in "Dark Corners of IronPython" by Michael Foord the clr.Convert function can be used to convert between types using the op_Implicit if necessary.

For example:


import clr
clr.AddReference("ImplicitTest")

from Langexplr.Experiments import *
from System import Double

c = clr.Convert(10.3, Complex)
nC = Complex()
nC.Real = 12.3
nC.Img = 5.2
p = clr.Convert(nC, Polar)
abs = clr.Convert(c, Double)

print 'abs: %(0)f Polar: %(1)f,%(2)f\n' % \
{ '0': abs, '1' : p.Angle, '2' : p.Length }


IronRuby



IronRuby will use the op_Implicit definition if a conversion required at a particular call. I couldn't find a nice way to do this directly as with IronPython's clr.Convert . However the following function definition seems to do the trick:


def dotnet_convert(value,type)
f = System::Func[type,type].new {|x| x}
f.invoke(value)
end


This conversion function works since IronRuby tries to convert the value to the expected .NET type in the call to 'invoke' .

Using this definition we can write:


require 'ImplicitTest.dll'

c = dotnet_convert(10.3,Langexplr::Experiments::Complex)
nC = Langexplr::Experiments::Complex.new
nC.Real = 12.3
nC.Img = 5.2
p = dotnet_convert(nC,Langexplr::Experiments::Polar)
abs = dotnet_convert(c,System::Double)

print "abs: #{abs} Polar: #{p.Angle},#{p.Length} \n"



F#



In F# we can call the op_Implicit method directly and F# will use type inference to determine the correct overload to use.

For example:

open Langexplr.Experiments

let c : Complex = Complex.op_Implicit 10.3
let p : Polar = Complex.op_Implicit (new Complex(Real=12.3, Img=5.2))
let abs : double = Complex.op_Implicit c

System.Console.WriteLine("1. {0} Polar: {1},{2} ", abs, p.Angle, p.Length )



There's a nice post called "F# – Duck Typing and Structural Typing" by Matthew Podwysocki, which describes a nice way to define a generic function to use the op_Implicit operator.


let inline convert (x:^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x )


This function can be used as follows:


let c2:Complex = convert 10.3
let p2:Polar = convert (new Complex(Real=12.3, Img=5.2))
let abs2:float = convert c

System.Console.WriteLine("2. {0} Polar: {1},{2} ",abs2,p2.Angle,p2.Length)


VB.NET



Finally in Visual Basic .NET the implicit conversion is used automatically as in C#. For example:


Imports System
Imports Langexplr.Experiments
Module Test
Sub Main
Dim c As Complex = 10.3
Dim p As Polar = new Complex() With { _
.Real = 12.3, _
.Img = 5.2 _
}
Dim abs As Double = c
Console.WriteLine("abs:{0} Polar: {1},{2}",abs,p.Angle,p.Length)
End Sub
End Module