I've got some C# classes, MyChar, Myint, MyDouble, that wrap char, int and double. Each has an implicit conversion operator from the wrapped type to the user-defined one. I've also got a set of overloaded functions, ToString(MyChar), ToString(MyInt), ToString(MyDouble).
I want to call ToString(MyChar) by passing in a literal char value, e.g. 'A'. But the compilation fails with CS0121, "The call is ambiguous between the following methods or properties: 'ToString(MyChar)' and 'ToString(MyInt)'". And I get a similar problem if I pass in an int value.
public class MyChar
{
private MyChar(char val) => Value = val;
public static implicit operator MyChar(char val) => new MyChar(val);
public char Value { get; }
}
public class MyInt
{
private MyInt(int val) => Value = val;
public static implicit operator MyInt(int val) => new MyInt(val);
public int Value { get; }
}
public class MyDouble
{
private MyDouble(double val) => Value = val;
public static implicit operator MyDouble(double val) => new MyDouble(val);
public double Value { get; }
}
public class ConversionTests
{
public void DoIt()
{
Console.WriteLine(ToString('A')); // CS0121
Console.WriteLine(ToString(1)); // CS0121
}
private static string ToString(MyChar c) => $"{c.Value}";
private static string ToString(MyInt i) => $"{i.Value}";
private static string ToString(MyDouble d) => $"{d.Value}";
}
I've found I can make the compiler accept the int value correctly, by adding an implicit conversion from MyInt to MyDouble
public class MyDouble
{
private MyDouble(double val) => Value = val;
public static implicit operator MyDouble(double val) => new MyDouble(val);
public static implicit operator MyDouble(MyInt val) => new MyDouble(val.Value);
public double Value { get; }
}
...
public void DoIt()
{
Console.WriteLine(ToString('A')); // CS0121
Console.WriteLine(ToString(1)); // now compiles :-)
}
I guess this works, because the resolution mechanism now thinks the conversion to MyDouble is now via the route 1 -> MyInt -> MyDouble, and this can't happen implicitly since it requires two user-defined conversions. In the same vein, I can get ToString('A')
to resolve correctly, by adding two more implicit conversions: MyChar to MyDouble, and MyChar to MyInt.
This is fine, since it is mirroring the implicit conversions that take place between char, int and double. But can anyone explain to me why the original code posted above won't compile without the additional conversions, in a way a simple brain like mine might understand?