Search code examples
c#genericstype-inferencemethod-resolution-order

C# method resolution with generic and type inference


I was surprised today by the way the method resolution works.

Here's the code as an exemple :

class Program
{
    static class Mapper<TSource, TTarget>
    {
        public static void Map<TMember>(Expression<Func<TSource, TMember>> source, Expression<Func<TTarget, TMember>> target)
        {
            Console.WriteLine("A");
        }

        public static void Map<TMember, TSourceCollection>(Expression<Func<TSource, TSourceCollection>> source, Expression<Func<TTarget, TMember[]>> target)
            where TSourceCollection : IEnumerable<TMember>
        {
            Console.WriteLine("B");
        }
    }

    class A
    {
        public byte[] prop { get; set; }
    }

    class B
    {
        public byte[] prop { get; set; }
    }

    static void Main(string[] args)
    {
        Mapper<A, B>.Map(x => x.prop, x => x.prop);
    }
}

As you see the method Map has two overloads, one when the type of the properties are the same, and one when the source property is an enumerable and the right property is an array.

Then when i call the method with an array on both sides it calls the second overload, but as the types are exactly the same i expected the first overload to be called.

I thought the first overload would have a better scoring because it depends on fewer generic arguments than the second, and it fits better with the types of the arguments i pass to the method.

Can someone explain why the compiler chooses to call the second overload instead of the first one please ?

Thanks.


Solution

  • Overload resolution is complicated and you can read the spec to understand why. One thing I am pretty sure of though is that it doesn't consider the need to specify less generic parameters when considering which overload is better (though it will take non-generic over generic but when both are generic it considers them equal).

    When looking at the overloads it can choose from they are all equal apart from whether the second parameter is TMember or TMember[].

    The spec talks a lot about choosing the most specific member and I can't work out which part is actually applicable here (there are many places where it talks about preferring X over Y when X is more specific). I would have thought it was either Section 7.6.5.1 (of the c#5 spec) which is where it constructs a candidate list or section 7.5.3 where it deals with overload resolution. However the former doesn't seem to exclude either method overload and the latter to my reading only deals with the parameters after the generic ones have been substituted in at which point they are identical. It may be that there is somewhere else in the spec that deals with this (eg when it infers the type arguments).

    In woolly terms though I believe that the compiler is considering that TMember[] is more specific than TMember. This can be broadly seen to be true because TMember will accept more things than TMember[] will so TMember[] is more specific.