Überladenes Methodengruppenargument verwirrt die Überladungsauflösung?

8

Der folgende Aufruf der überladenen Enumerable.Select Methode:

%Vor%

schlägt mit einem Mehrdeutigkeitsfehler fehl (Namespaces wurden aus Gründen der Übersichtlichkeit entfernt):

%Vor%

Ich kann sicherlich verstehen, warum nicht die Typargumente explizit angibt, würde zu einer Mehrdeutigkeit führen (beide Überladungen würden gelten), aber ich sehe keine danach.

Es scheint mir klar genug zu sein, dass die erste Überladung aufgerufen werden soll, wobei das Argument der Methodengruppe in Tuple.Create<char>(char) aufgelöst wird. Die zweite Überladung sollte nicht angewendet werden, da keine der Tuple.Create Überladungen in den erwarteten Func<char,int,Tuple<char>> Typ konvertiert werden kann. Ich bin raten der Compiler ist durch Tuple.Create<char, int>(char, int) verwirrt, aber sein Rückgabetyp ist falsch: Er gibt ein Zwei-Tupel zurück und ist daher nicht in den relevanten Func -Typ konvertierbar.

Übrigens macht einer der folgenden Punkte den Compiler glücklich:

  1. Angeben eines Typarguments für das Methodengruppenargument: Tuple.Create<char> (Vielleicht handelt es sich hierbei um ein Problem mit Typinferenz?).
  2. Das Argument wird zu einem Lambda-Ausdruck anstelle einer Methodengruppe: x => Tuple.Create(x) . (Spielt gut mit Typinferenz am Select -Aufruf).

Es überrascht nicht, dass der Versuch, die andere Überladung von Select auf diese Weise aufzurufen, fehlschlägt:

%Vor%

Was ist das genaue Problem hier?

    
Ani 05.03.2011, 12:25
quelle

2 Antworten

20

Zuerst stelle ich fest, dass dies ein Duplikat von:

ist

Warum ist Func & lt; T & gt; mehrdeutig mit Func & lt; IEnumerable & lt; T & gt; & gt ;;

  

Was ist das genaue Problem hier?

Thomas 'Vermutung ist im Wesentlichen richtig. Hier sind die genauen Details.

Lasst uns Schritt für Schritt durchgehen. Wir haben eine Invokation:

%Vor%

Die Überladungsauflösung muss die Bedeutung des Aufrufs von Select bestimmen. Es gibt keine Methode "Select" für eine Zeichenfolge oder eine Basisklasse von string, daher muss dies eine Erweiterungsmethode sein.

Es gibt eine Reihe möglicher Erweiterungsmethoden für den Kandidatensatz, da der String in IEnumerable<char> konvertierbar ist und vermutlich dort irgendwo using System.Linq; ist. Es gibt viele Erweiterungsmethoden, die mit dem Muster "Select, generic arity two, nimmt ein IEnumerable<char> als erstes Argument, wenn mit den angegebenen Methoden Typ Argumente" erstellt werden.

Insbesondere sind zwei der Kandidaten :

%Vor%

Nun, die erste Frage, der wir gegenüberstehen, sind die Kandidaten anwendbar ? Das heißt, gibt es eine implizite Konvertierung von jedem übergebenen Argument zu dem entsprechenden Formalparametertyp?

Eine ausgezeichnete Frage. Offensichtlich ist das erste Argument der "Empfänger", eine Zeichenkette, und es wird implizit in IEnumerable<char> konvertiert. Die Frage ist nun, ob das zweite Argument, die Methodengruppe "Tuple.Create", implizit in die Formalparameter Typen Func<char,Tuple<char>> und Func<char,int, Tuple<char>> umwandelbar ist.

Wann kann eine Methodengruppe in einen bestimmten Delegattyp umgewandelt werden? Eine Methodengruppe kann in einen Delegattyp konvertiert werden, wenn die Überladungsauflösung Argumente der gleichen Typen wie die formalen Parameter des Delegate erhalten hätte.

Das heißt, M ist in Func<A, R> konvertierbar, wenn die Überladungsauflösung für einen Aufruf der Form M(someA) erfolgreich gewesen wäre, wenn ein Ausdruck 'someA' vom Typ 'A' gegeben wäre.

Wäre die Überladungsauflösung bei einem Aufruf von Tuple.Create(someChar) erfolgreich? Ja; Überladungsauflösung hätte Tuple.Create<char>(char) gewählt.

Wäre die Überladungsauflösung bei einem Aufruf von Tuple.Create(someChar, someInt) erfolgreich? Ja, die Überladungsauflösung hätte Tuple.Create<char,int>(char, int) gewählt.

Da in beiden Fällen die Überladungsauflösung erfolgreich gewesen wäre, kann die Methodengruppe in beide Delegattypen konvertiert werden. Die Tatsache, dass der Rückgabetyp einer der Methoden nicht mit dem Rückgabetyp des Delegaten übereinstimmt, ist irrelevant. Die Überladungsauflösung ist aufgrund der Rückgabetypanalyse nicht erfolgreich oder schlägt fehl .

Man könnte vernünftigerweise sagen, dass Konvertierbarkeit von Methodengruppen zu Delegattypen basierend auf der Rückgabetypanalyse erfolgreich sein oder fehlschlagen sollte, aber so ist die Sprache nicht angegeben. Die Sprache wird angegeben, um die Überladungsauflösung als Test für die Methodengruppenumwandlung zu verwenden, und ich denke, das ist eine vernünftige Wahl.

Deshalb haben wir zwei geeignete Kandidaten. Gibt es eine Möglichkeit, dass wir entscheiden können, was besser ist als das andere? Die Spezifikation besagt, dass die Konvertierung in den spezifischeren Typ besser ist; wenn du

hast %Vor%

Dann wählt die Überladungsauflösung die Zeichenfolgenversion, da die Zeichenfolge spezifischer als das Objekt ist. Ist einer dieser Delegattypen spezifischer als der andere? Nein, keiner ist spezifischer als der andere. (Dies ist eine Vereinfachung der besseren Konvertierungsregeln; es gibt tatsächlich viele Tiebreaker, aber keiner von ihnen gilt hier.)

Daher gibt es keine Grundlage, die eine vor der anderen zu bevorzugen.

Auch hier könnte man vernünftigerweise sagen, dass es eine Basis gibt, nämlich dass eine dieser Konvertierungen einen Delay-Typ-Mismatch-Fehler erzeugt und einer von ihnen nicht. Die Sprache wird jedoch erneut angegeben, um die Qualität zu verbessern, indem die Beziehungen zwischen den formalen Parametertypen berücksichtigt werden und nicht, ob die gewählte Konvertierung schließlich zu einem Fehler führt.

Da es keine Grundlage gibt, auf der man die eine vor der anderen bevorzugen könnte, ist dies ein Mehrdeutigkeitsfehler.

Es ist einfach, ähnliche Mehrdeutigkeitsfehler zu konstruieren. Zum Beispiel:

%Vor%

Das ist mehrdeutig. Auch wenn es illegal ist, ein ++ in einem Ausdrucksbaum zu haben, berücksichtigt die Konvertierbarkeitslogik nicht, ob der Körper eines Lambda etwas in sich hat, das in einem Ausdrucksbaum illegal wäre . Die Konvertierungslogik stellt nur sicher, dass die Typen auschecken, und das tun sie auch. Angesichts dessen gibt es keinen Grund, eines der M's dem anderen vorzuziehen, also ist dies eine Zweideutigkeit.

Sie merken das

%Vor%

ist erfolgreich. Du weißt jetzt warum. Überladungsauflösung muss bestimmen, ob

%Vor%

oder

%Vor%

würde gelingen. Da der erste und der zweite nicht funktionieren, ist der zweite Kandidat nicht anwendbar und eliminiert, und ist daher nicht dazu da, mehrdeutig zu werden.

Sie merken das auch

%Vor%

ist eindeutig. Lambda-Konvertierungen do berücksichtigen die Kompatibilität des zurückgegebenen Ausdruckstyps mit dem Rückgabetyp des Zieldelegaten. Es ist bedauerlich, dass Methodengruppen und Lambda-Ausdrücke zwei subtil verschiedene Algorithmen verwenden, um die Konvertierbarkeit zu bestimmen, aber wir stecken jetzt fest damit. Denken Sie daran, dass Methodengruppenkonvertierungen in der Sprache viel länger waren als Lambda-Conversions. wären sie zur gleichen Zeit hinzugefügt worden, stelle ich mir vor, dass ihre Regeln konsistent gemacht worden wären.

    
Eric Lippert 09.03.2011, 22:40
quelle
5
  

Ich vermute, der Compiler ist durch Tuple.Create<char, int>(char, int) verwirrt, aber sein Rückgabetyp ist falsch: Er gibt ein Zwei-Tupel zurück.

Der Rückgabetyp ist nicht Teil der Methodensignatur und wird daher bei der Überladungsauflösung nicht berücksichtigt. es ist nur verifiziert nachdem eine Überladung ausgewählt wurde. Soweit der Compiler weiß, ist Tuple.Create<char, int>(char, int) ein gültiger Kandidat und weder besser noch schlechter als Tuple.Create<char>(char) , so dass der Compiler nicht entscheiden kann.

    
Thomas Levesque 05.03.2011 13:49
quelle