10

I try to find some information about method overloading resolution in case of presence of user-defined implicit conversions and about conversions priority.

This code:

class Value
{
    private readonly int _value;
    public Value(int val)
    {
        _value = val;
    }

    public static implicit operator int(Value value)
    {
        return value._value;
    }

    public static implicit operator Value(int value)
    {
        return new Value(value);
    }
}

class Program
{
    static void ProcessValue(double value)
    {
        Console.WriteLine("Process double");
    }

    static void ProcessValue(Value value)
    {
        Console.WriteLine("Process Value");
    }

    static void Main(string[] args)
    {
        ProcessValue(new Value(10));
        ProcessValue(10);
        Console.ReadLine();
    }
}

Produces output:

Process Value
Process Value

So, It looks like compiler chosen user-defined conversion instead of built-in implicit conversion from int to double (built-in conversion is implicit due to info from this page https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/implicit-numeric-conversions-table).

I tried to find something about this in specification, but with no success.

Why compiler selected ProcessValue(Value value) instead of ProcessValue(double value)

Nkosi
  • 235,767
  • 35
  • 427
  • 472
Daniil Vlasenko
  • 457
  • 3
  • 11
  • It will not do two conversions, so it won't convert it to double and then double to Value, it will instead look for a direct cast/conversion. – Lasse V. Karlsen Nov 12 '18 at 13:31
  • 2
    @LasseVågsætherKarlsen: The OP isn't expecting it to do two conversions. They're just expecting it to do a conversion from `int` to `double` (or perhaps treat it as ambiguous) – Jon Skeet Nov 12 '18 at 13:37
  • 1
    At the moment, this looks like a compiler or spec bug to me. I think it should be ambiguous. – Jon Skeet Nov 12 '18 at 13:43
  • @Jon Skeet Or built-in conversion should be first in list of possible conversions. It looks like now it is able to change semantics of existing code just by adding user-defined conversion – Daniil Vlasenko Nov 12 '18 at 13:45
  • 1
    @DanielVlasenko: Oh you can *definitely* change the semantics of existing code just by adding a user-defined conversion. That's easy to give examples of. But this should at least be explicable in the spec... – Jon Skeet Nov 12 '18 at 13:51

1 Answers1

4

In this case, the conversion between int -> double takes lower precedence because a user-defined implicit conversion between int -> Value exists.

See: User-defined operator implementations always take precedence over predefined operator implementations.

Leonardo Herrera
  • 8,388
  • 5
  • 36
  • 66
  • 1
    Just for some extra context for OP: `10` is of type `int` while `10.0` is of type `double`. – Stefan Nov 12 '18 at 13:29
  • 3
    Now that I read the OP question more carefully, I think he knows this. I'll keep this answer around for a bit just to prevent others to answer the same . – Leonardo Herrera Nov 12 '18 at 13:31
  • Yes, sir, I know about literal types. And of course if I'll use 10.o instead of 10 ProcessValue(double) will be called. My question is about conversion priority, why i->double is lower than user-defined int->Value – Daniil Vlasenko Nov 12 '18 at 13:37
  • That "precedence" is only used within operator overload resolution, which isn't being used here. In this case, both methods are applicable, and the rules for "better function member" should apply. If the intention is for a user-defined operator implementation to "win" here, it should be shown in "better conversion target". I really don't think the spec handles this situation at the moment. – Jon Skeet Nov 13 '18 at 07:30
  • Ouch, you are right. But reading the [documentation](https://www.microsoft.com/en-us/download/confirmation.aspx?id=7029) I _think_ it does handles this by stating that implicit conversion for arguments matters, and for tie-breaking the more specific parameter types are better (7.5.3.2). – Leonardo Herrera Nov 13 '18 at 12:58
  • So yes, we have to choose which conversion to use and we see this sentence about user-defined conversions and we could guess that it is also could be applied to method overload resolution. But. It is still about resolution of conversion operators. int->double is not a conversion operator as I understand, it is a built-in conversion and int->Value is user-defined conversion defined through conversion operator overloading. If int->double is an conversion operator by spec, than this is an answer – Daniil Vlasenko Nov 13 '18 at 13:45
  • the link is broken – David Lechner Jul 08 '19 at 02:07
  • @DavidLechner thank you, replaced it with a new one – Leonardo Herrera Jul 08 '19 at 19:03