Search code examples
c#compiler-errorsuser-defined

No compilation error when the "same" user-defined conversion exists twice


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?


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);