Разрешение метода C# с обобщением и выводом типа

Сегодня я был удивлен тем, как работает метод разрешения.

Вот код в качестве примера:

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

Как вы видите, метод Map имеет две перегрузки: одна, когда тип свойств одинаков, и одна, когда свойство source является перечислимым, а свойство right является массивом.

Затем, когда я вызываю метод с массивом с обеих сторон, он вызывает вторую перегрузку, но так как типы в точности совпадают, я ожидал, что будет вызвана первая перегрузка.

Я думал, что первая перегрузка будет иметь лучшую оценку, потому что она зависит от меньшего количества общих аргументов, чем вторая, и она лучше соответствует типам аргументов, которые я передаю методу.

Может кто-нибудь объяснить, почему компилятор предпочитает вызывать вторую перегрузку вместо первой, пожалуйста?

Благодарю.

2 ответа

Решение

Разрешение перегрузки является сложным, и вы можете прочитать спецификацию, чтобы понять, почему. В одном я уверен, что он не учитывает необходимость указания менее универсальных параметров при рассмотрении того, какая перегрузка лучше (хотя он будет не универсальным по сравнению с универсальным, но когда оба являются общими, он считает их равными).

При рассмотрении перегрузок можно выбрать, все ли они равны, кроме того, является ли второй параметр TMember или же TMember[],

В спецификации много говорится о выборе наиболее конкретного члена, и я не могу понять, какая часть на самом деле применима здесь (есть много мест, где говорится о предпочтении X над Y, когда X более конкретен). Я бы подумал, что это был либо раздел 7.6.5.1 (спецификации C#5), в котором он создает список кандидатов, либо раздел 7.5.3, где он занимается разрешением перегрузки. Однако первое, похоже, не исключает перегрузки ни одного из методов, а последнее, на мой взгляд, имеет дело только с параметрами после замены общих, в этот момент они идентичны. Возможно, что в спецификации есть что-то еще, что имеет дело с этим (например, когда он выводит аргументы типа).

В целом, хотя я считаю, что компилятор считает, что TMember[] более конкретно, чем TMember, Это в целом можно увидеть, чтобы быть правдой, потому что TMember будет принимать больше вещей, чем TMember[] будет так TMember[] более конкретно.

Соответствие первого метода TMember и второй метод TSourceCollection имеет равное значение для любого типа, который удовлетворяет where TSourceCollection : IEnumerable<TMember> состояние.

Тип TMember[] более подробный тип соответствия для byte[] по сравнению с TMember, Так что это должен быть момент, когда второй метод получает лучшие результаты, чем первый. Следовательно, это "лучшее" соответствие исключает тот факт, что второй метод имеет более общие параметры.

Другие вопросы по тегам