Разрешение метода 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
, Так что это должен быть момент, когда второй метод получает лучшие результаты, чем первый. Следовательно, это "лучшее" соответствие исключает тот факт, что второй метод имеет более общие параметры.