Frage

Ich frage mich, warum der C # 3.0-Compiler ist nicht in der Lage, die Art eines Verfahrens zu schließen, wenn sie als Parameter an eine generischen Funktion übergeben wird, wenn er implizit einen Delegaten für die gleiche Methode erstellen kann.

Hier ist ein Beispiel:

class Test
{
    static void foo(int x) { }
    static void bar<T>(Action<T> f) { }

    static void test()
    {
        Action<int> f = foo; // I can do this
        bar(f); // and then do this
        bar(foo); // but this does not work
    }   
}

Ich hätte gedacht, dass ich in der Lage wäre foo passieren bar und haben die Compiler den Typen des Action<T> von der Signatur der Funktion ableiten, übergeben wird, aber das funktioniert nicht. Allerdings kann ich eine Action<int> von foo erstellen, ohne so zu Gießen gibt es einen legitimen Grund, dass der Compiler nicht auch die gleiche Sache über Typinferenz tun könnte?

War es hilfreich?

Lösung

Vielleicht wird es deutlicher:

public class SomeClass
{
    static void foo(int x) { }
    static void foo(string s) { }
    static void bar<T>(Action<T> f){}
    static void barz(Action<int> f) { }
    static void test()
    {
        Action<int> f = foo;
        bar(f);
        barz(foo);
        bar(foo);
        //these help the compiler to know which types to use
        bar<int>(foo);
        bar( (int i) => foo(i));
    }
}

foo ist nicht eine Aktion - foo ist eine Methode Gruppe

.
  • In der Zuweisungsanweisung kann der Compiler sagt klar, welche foo Sie sprechen, da der int-Typ angegeben ist.
  • Im barz (foo) Anweisung kann der Compiler sagen, welche foo Sie sprechen, da der int-Typ angegeben ist.
  • In der Bar (foo) Aussage, es könnte jeder foo mit einem einzigen Parameter - also der Compiler gibt auf
  • .

Edit:. Ich habe zwei (mehr) Möglichkeiten hinzugefügt, um den Compiler herauszufinden, die Art zu helfen (dh - wie die Inferenz Schritte überspringen)

Aus meiner Lektüre des Artikels in JSkeet Antwort, die Entscheidung, nicht die Art zu schließen, scheint auf einem gegenseitigen infering Szenario basieren, wie zum Beispiel

  static void foo<T>(T x) { }
  static void bar<T>(Action<T> f) { }
  static void test()
  {
    bar(foo); //wut's T?
  }

Da das allgemeine Problem war unsolve-fähig, wählen sie bestimmte Probleme nach links, wo eine Lösung als ungelöst vorhanden ist.

Als Folge dieser Entscheidung werden Sie nicht eine Überlastung für ein Verfahren des Hinzufügen und eine ganze Menge Verwirrung Typen von allen Anrufern erhalten, die zu einer einzelnen Element-Methode Gruppe verwendet werden. Ich denke, das ist eine gute Sache.

Andere Tipps

Die Argumentation ist, dass, wenn der Typ überhaupt dort erweitert keine Möglichkeit des Scheiterns sein sollte. das heißt, wenn eine Methode foo (string) den Typen hinzugefügt wird, soll es nie zu vorhandenen Code egal - solange der Inhalt der bestehenden Methoden nicht ändern

.

Aus diesem Grund kann, selbst wenn es nur eine Methode foo, ein Verweis auf foo (als Methode Gruppe bekannt) kann nicht auf einen nicht-typspezifischen Delegierten gegossen werden, wie Action<T> sondern nur auf einen typspezifischen Delegat wie Action<int>.

Das ist etwas seltsam, ja. Die C # 3.0-Spezifikation für Typinferenz ist schwer zu lesen und hat Fehler drin, aber es sieht wie es funktionieren soll. In der ersten Phase (Abschnitt 7.4.2.1) Ich glaube, es ist ein Fehler - es ist nicht Methode Gruppen im ersten Geschoss erwähnen sollte (wie sie durch explizite Parameter Typinferenz nicht abgedeckt ist (7.4.2.7) - was bedeutet, sollte es verwenden . Ausgang Typinferenz (7.4.2.6) Die sieht wie es funktionieren soll - aber es hat offenbar nicht: (

Ich weiß, dass MS sucht die Spezifikation für Typinferenz zu verbessern, so könnte es ein wenig klarer geworden. Ich weiß auch, dass unabhängig von der Schwierigkeit, es zu lesen, gibt es Beschränkungen für Methodengruppen und Typinferenz -. Einschränkungen, die besondere Gefasste sein könnte, wenn die Methode Gruppe eigentlich nur eine einzige Methode ist, zugegebenermaßen

Eric Lippert hat einen Blog-Eintrag auf andere Beiträge in seiner Typinferenz Serie kann jedoch helfen.

Halten Sie

Sie daran, dass die Zuordnung

Action<int> f = foo;

hat bereits eine Menge syntaktischer Zucker. Der Compiler generiert tatsächlich Code für diese Aussage:

Action<int> f = new Action<int>(foo);

Der entsprechende Methodenaufruf kompiliert ohne Problem:

bar(new Action<int>(foo));

FWIW, so auch helfen, die Compiler den Typ Argument ableiten:

bar<int>(foo);

So ist es läuft auf die Frage hinaus, warum der Zucker in der Erklärung Zuordnung aber nicht in dem Methodenaufruf? Ich würde erraten, dass es ist, weil der Zucker in der Zuordnung eindeutig ist, gibt es nur eine mögliche Substitution. Aber im Fall von Methodenaufrufen hatten die Compiler Schriftsteller bereits mit der Überladungsauflösung Problem zu beschäftigen. Die Regeln von denen sind ziemlich aufwendig. Wahrscheinlich haben sie einfach nicht herumkommen, um es.

Nur der Vollständigkeit halber ist dies in C # nicht spezifisch: Das gleiche VB.NET-Code nicht in ähnlicher Weise:

Imports System

Module Test
  Sub foo(ByVal x As integer)
  End Sub
  Sub bar(Of T)(ByVal f As Action(Of T))
  End Sub

  Sub Main()
    Dim f As Action(Of integer) = AddressOf foo ' I can do this
    bar(f) ' and then do this
    bar(AddressOf foo) ' but this does not work
  End Sub
End Module

Fehler BC32050:. Typ Parameter 'T' für 'Public Sub bar (Of T) (f Wie System.Action (Of T))' kann nicht gefolgert werden,

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top