2016-06-16 4 views
2

Я был удивлен сегодня тем, как работает разрешение метода.Разрешение метода C# с общим и типом вывода

Вот код, как 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); 
    } 
} 

Как вы видите, метод Карта имеет два перегруженных, один, когда тип свойства одинаковы, и один, когда свойство источника перечислимого и right - массив.

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

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

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

Спасибо.

+0

Вы называете преобразователь классом A и B, которые не являются массивами. Оба они имеют свойство, которое является байтовым массивом, но они не одного типа. – Glubus

+0

Используется изменение байта [] на int и первое. Поэтому я предполагаю, что это потому, что байтовый массив является IEnumerable. – user743414

+0

Спасибо, но разрешение должно возникнуть на TMember, а не на TSource или TTarget. TSource и TTarget отличаются по своей природе. –

ответ

1

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

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

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

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

+0

Спасибо за великолепное объяснение, это имеет смысл сейчас. –

1

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

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