2016-06-16 21 views
2

Ich war heute überrascht wie die Methodenauflösung funktioniert.C# Methodenauflösung mit generischer und typischer Inferenz

Hier ist der Code als 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); 
    } 
} 

Wie Sie sehen die Methode Karte zwei Überlastungen hat, ein, wenn die Art der Eigenschaften sind die gleichen, und man, wenn die Quelleigenschaft eine zählbare ist und die rechte Eigenschaft ist ein Array.

Dann wenn ich die Methode mit einem Array auf beiden Seiten aufrufen, ruft es die zweite Überladung, aber da die Typen genau gleich sind, erwartete ich die erste Überladung aufgerufen werden.

Ich dachte, die erste Überladung hätte eine bessere Bewertung, weil sie von weniger generischen Argumenten abhängt als die zweite, und sie passt besser zu den Typen der Argumente, die ich an die Methode übergebe.

Kann jemand erklären, warum der Compiler wählt, die zweite Überladung anstelle der ersten zu nennen?

Danke.

+0

Sie nennen den Mapper mit einer Klasse A und B, die keine Arrays sind. Beide haben eine Eigenschaft, die ein Byte-Array ist, aber sie sind nicht vom selben Typ. – Glubus

+0

Ändern Sie Byte [] in int und die erste wird verwendet. Ich denke also, weil ein Byte-Array ein IEnumerable ist. – user743414

+0

Danke aber die Auflösung sollte auf TMember, nicht TSource oder TTarget auftreten. Die TSource und TTarget sind von Natur aus unterschiedlich. –

Antwort

1

Überladungsauflösung ist kompliziert und Sie können die Spezifikation lesen, um zu verstehen, warum. Eine Sache, die ich ziemlich sicher bin, ist, dass es nicht die Notwendigkeit berücksichtigt, weniger generische Parameter zu spezifizieren, wenn überlegen ist, welche Überladung besser ist (obwohl es nicht-generisch über generisch ist, aber wenn beide generisch sind, hält es sie für gleich).

Bei der Betrachtung der Überladungen kann sie wählen, ob sie alle gleich sind, abgesehen davon, ob der zweite Parameter TMember oder TMember[] ist.

Die Spezifikation spricht viel über die Auswahl des spezifischsten Mitglieds und ich kann nicht herausfinden, welcher Teil tatsächlich hier anwendbar ist (es gibt viele Orte, an denen es X gegenüber Y bevorzugt, wenn X spezifischer ist). Ich hätte gedacht, dass es entweder Abschnitt 7.6.5.1 (der C# 5-Spezifikation) ist, wo es eine Kandidatenliste erstellt, oder Abschnitt 7.5.3, wo es sich mit der Überladungsauflösung befasst. Die erstere scheint jedoch keine der Methodenüberladungen auszuschließen, und die letztere bezieht sich nur auf die Parameter, nachdem die generischen ersetzt wurden und zu dem Zeitpunkt, an dem sie identisch sind. Es kann sein, dass es dort woanders in der Spezifikation ist, die sich damit beschäftigt (zB wenn es die Typargumente ableitet).

In wolliger Hinsicht, obwohl ich glaube, dass der Compiler in Betracht zieht, dass TMember[] spezifischer ist als TMember. Dies kann allgemein als zutreffend angesehen werden, da TMember mehr Dinge akzeptiert als TMember [], so dass TMember[] spezifischer ist.

+0

Danke für die großartige Erklärung, die jetzt Sinn macht. –

1

Die Übereinstimmung der ersten Methode TMember und der zweiten Methode TSourceCollection ist für jeden Typ gleichwertig, der die Bedingung where TSourceCollection : IEnumerable<TMember> erfüllt. Der Typ TMember[] ist eine detailliertere Typübereinstimmung für byte[] im Vergleich zu TMember. Das sollte der Punkt sein, an dem die zweite Methode besser abschneidet als die erste. Folglich "outrules" diese "bessere" Übereinstimmung die Tatsache, dass Methode zwei allgemeinere Parameter hat.