Two-way implicit casting

I’m presently writing a framework that wraps a C API using interop. Many of the classes/structs defined in the C library already exist in .NET, but I have to re-implement them anyway so that I can interface with the library. People accustomed to the .NET classes won’t be fond of this because they already have a set of classes that work well, and the new ones won’t work with existing .NET methods. There is a simple solution to this, however! Implicit casting!

Here’s a sample of how I made my Color struct compatible with the one defined in System.Drawing.Color:

[StructLayout(LayoutKind.Sequential)]
public struct Color
{
    public byte R, G, B, A;

    public static implicit operator System.Drawing.Color(Color c)
    {
        return System.Drawing.Color.FromArgb(c.A, c.R, c.G, c.B);
    }

    public static implicit operator Color(System.Drawing.Color c)
    {
        return new Color {R = c.R, G = c.G, B = c.B, A = c.A};
    }
}

Note that System.Drawing.Color uses ints instead of bytes for its colors, and they aren’t necessarily stored in the same memory location, so I couldn’t use that class directly. You can, however, convert back and forth between the two classes without any loss of data, so this is a perfect case for implicit casting. If the two classes aren’t convertible without some data changing, you should use explicit casting instead, or perhaps a static factory method.

I was curious, however, to know what would happen if an implicit casting method was defined in both classes. Here’s an example:

class A
{
    public static implicit operator B(A a)
    {
        Console.WriteLine("A::B");
        return new B();
    }
}

class B
{
    public static implicit operator B(A a)
    {
        Console.WriteLine("B::B");
        return new B();
    }
}

class Program
{
    public static void F(B b)
    {
        Console.WriteLine("F()");
    }

    static void Main(string[] args)
    {
        var a = new A();
        F(a);
        Console.ReadLine();
    }
}

How does it know which method to call? The answer: it doesn’t! This actually gives the following error:

Ambiguous user defined conversions ‘ImplicitTest.A.implicit operator ImplicitTest.B(ImplicitTest.A)’ and ‘ImplicitTest.B.implicit operator ImplicitTest.B(ImplicitTest.A)’ when converting from ‘ImplicitTest.A’ to ‘ImplicitTest.B’

There is a way you can still utilize one of conversion methods, but it isn’t pretty. Here’s the solution work-around:

static void Main(string[] args)
{
    var method = typeof (A).GetMethod("op_Implicit", new[] {typeof (A)});
    var converter = (Func<A, B>) Delegate.CreateDelegate(typeof (Func<A, B>), method);
    var a = new A();
    F(converter.Invoke(a));
    Console.ReadLine();
}

[credit]

The real solution would be to just delete one of the methods, if you can!

Posted in

Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *