Question

In C#, why are there no compilation errors when the "same" user-defined conversion exists twice? (once in the source class and once in the target class?)

For example, if I try to compile the following code I get no compilation errors:

namespace TestConversionOverloading
{
    public class A
    {
        public int value = 1;

        public static explicit operator B(A a)
        {
            B b = new B();

            b.value = a.value + 6;

            return b;
        }
    }

    public class B
    {
        public int value = 2;

        public static explicit operator B(A a)
        {
            B newB = new B();

            newB.value = a.value;

            return newB;
        }
    }

    public class program
    {
        public static void Main() {}
    }
}

However, if I try to explicitly convert A to B, I do get a compilation error. Say I add the following to Main() and try to compile:

A a = new A();
B b = ((B)a);

I'll get the following:

Ambiguous user defined conversions 'TestConversionOverloading.A.explicit operator TestConversionOverloading.B(TestConversionOverloading.A)'
and 'TestConversionOverloading.B.explicit operator TestConversionOverloading.B(TestConversionOverloading.A)'
when converting from 'TestConversionOverloading.A' to 'TestConversionOverloading.B'

So why not give an error straight from definition? Could there be a way to use either conversion?

Was it helpful?

Solution

I wouldn't speculate on why it makes sense for the language to allow this, but if you are in control of both classes, the obvious solution is to get rid of one of the operators.

If you can't, here's a way to disambiguate using reflection.

First, create a delegate that binds to the intended operator:

// Picks the conversion operator declared in class A.
var method = typeof(A).GetMethod("op_Explicit", new[] { typeof(A) });
var converter = (Func<A, B>)Delegate.CreateDelegate(typeof(Func<A, B>), method);

And then use the delegate as:

A a = ...
B b = converter(a);

OTHER TIPS

According to the spec, this is the expected behavior.

Heavily compressing the original text, here's what happens in this case: the compiler will find all operators that could convert A to B in both class definitions. This would enlist A operator B(A a) and B operator B(A a). Then,

If no such operator exists, or if more than one such operator exists, then the conversion is ambiguous and a compile-time error occurs.

So why not give an error straight from definition? Because both definitions are OK, but it's their use that makes the problem arise.

Could there be a way to use either conversion? I don't see an easy way to do this. I'm thinking of bypassing the compiler, emitting IL by hand. That way I think you can instruct the program to use one operator or the other. Not sure if this is entirely feasible, though. A tool like Reflector could help.

While there's some beauty using operator-based conversions, either one of the classes will lose one operator, or you could change to constructor-based conversions or a more straightforward syntax of ToA(A a) and FromA(A a). Or maybe Eric Lippert could enlighten us with some language cleverness!

Look at the IL code generated per "public static implicit operator B(A a)" code line:

.method public hidebysig specialname static
class TestConversionOverloading.B  op_Explicit(class TestConversionOverloading.A a) cil managed

So here is the answer on the first question: Implicit/explicit convertion operators are syntactic sugar. In MSIL they are look like usual methods (and they are). There is nothing criminal when two different classes have methods with identical signature as it does not violate anything. Though conversion operator call cannot be compiled in this case. And as it was mentioned you can use reflection to get MethodInfo of either method.

Remember that one of the conflicting conversions could be generic, and could be useful for other combinations of generic parameters.

You can even have conflicting conversions defined in the SAME class:

class C<T>
{
    implicit operator int() { return 0; }
    implicit operator T() { return default(T); }
}

C<int> c;
int i = c;

If the compiler complained about this, you'd lose the ability for C<string> to convert to both string and int.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top