2016-07-22 4 views
1

Ich habe folgendes Beispiel.Warum verursacht dies den Fehler CS4011: Argumente vom Typ können nicht aus der Verwendung abgeleitet werden?

class Program { 
    static void Main(string[] args) { 
     var varc = new C(); 
     // Error CS0411 
     var varf1b = varc.F1(t => new { City = t.City }, (t, v) => v + t.Status, (g, a) => new { City = g.City, Bigstatus = a }); 
     // compiles OK 
     var varf1a = varc.F1(t => new { City = t.City }, (C t, int v) => v + t.Status, (g, a) => new { City = g.City, Bigstatus = a }); 

     // now replace varc by an anonymous class -- how to solve it now? 
     var varanon = new { }; 

    } 
    } 

    public class C { 
    public string City; 
    public int Status; 
    } 

    public static class X { 
    public static T1 F1<T2, T3, T4, T1>(
     this T2 s, 
     Func<T2, T3> g, 
     Func<T2, T4, T4> a, 
     Func<T3, T4, T1> r) 
     where T2 : class where T3 : class where T1 : class { 
     return null; 
    } 
    } 

Der erste Aufruf löst den Fehler CS0411 aus. Ich kann das nicht mit der üblichen Methode beheben, die die Typargumente hinzufügt, weil sie anonyme Typen verwendet. Glücklicherweise scheint das Hinzufügen von Typen zu einem der Lambdas ausreichend zu sein, um den Compiler glücklich zu machen.

Warum ist das? Was genau macht das erste Beispiel fehl und das zweite gelingt?

Und zweitens gibt es eine Möglichkeit, den Funktionsaufruf zu schreiben, so wird dies nicht passieren, und der Benutzer ist nicht konfrontiert mit dem Einfügen von Typen in das Lambda?

Ja, andere haben ähnliche Fragen gestellt, aber was hier einzigartig ist, ist (a) die Verwendung von anonymen Typen (b) das Update durch Hinzufügen von Typen zum Lambda.


Editiert: das Problem mit der Lösung gegeben ist, dass es nicht mit anonymen Klassen verwendet werden kann, weil es keine Typanmerkung möglich ist.

+1

Sieh es so, du erwartest, dass der Compiler weiß, dass 'v' vom Typ' int' ist. Aber warum sollte es möglich sein? 'T4' könnte leicht' long', 'double',' dezimal' oder jeder andere Typ sein, der den Additionsoperator mit ganzen Zahlen überlädt. –

+0

@JeffMercado: Nein, das ist es nicht. Der Compiler ist nicht an der Form des Ausdrucks interessiert: Sie können ihn durch irgendetwas ersetzen (gültig), ohne den Fehler zu ändern. –

+0

Mein Punkt ist, dass der Compiler nicht zu weit gehen wird, um zu versuchen, die Typen für Sie zu bestimmen, zumindest nicht so weit wie andere Sprachen.Und selbst wenn es versuchen würde, würde es nicht gelingen, weil die Wahl mehrdeutig wäre. Es wird nur die Typen verwenden, von denen es vollständig bekannt war. Sie können keine Informationen über die Eingabetypen bereitstellen, aber die Ausgabetypen und alles, was Sie angeben, ist, dass 'T4' das Ergebnis des Hinzufügens eines' T4' mit einem 'int' sein könnte, was nicht genug Information ist. –

Antwort

1

Die Frage ist: Was ist der Typ von v?

Der Compiler leitet den Typ der Eingabe aus dem als Ausgabe verwendeten Ausdruck nicht ab. Der Compiler verwendet den abgeleiteten Typ der Eingabe, damit Sie einen gültigen Ausdruck für die Ausgabe schreiben können. So bedeutet (t, v) => 1 nicht, dass v vom Typ int

Dies wird der Compiler versuchen, Ihren ersten Versuch zu analysieren:

var varf1b = varc.F1(t => new { City = t.City }, (t, v) => v + t.Status, (g, a) => new { City = g.City, Bigstatus = a });   
        |   |     | |     | |       | 
        |   |     | |     | |       | 
       T2: C (varc)  |     | ?     | ?       | 
           |     |      |     T1: new { string City, ? Bigstatus } 
         T3: new { string City }  |      | 
               T2: C     | 
                    T3: new { string City } 

Dies wird der Compiler versuchen, Ihren zweiten Versuch zu analysieren:

var varf1b = varc.F1(t => new { City = t.City }, (C t, int v) => v + t.Status, (g, a) => new { City = g.City, Bigstatus = a });   
        |   |     |  |     | |       | 
        |   |     |  |     | |       | 
       T2: C (varc)  |     | T4: int    | T4: int     | 
           |     |       |     T1: new { string City, int Bigstatus } 
         T3: new { string City }  |       | 
                T2: C      | 
                      T3: new { string City } 

also varf1b ist vom Typ: T1: new { string City, int Bigstatus } und die Inferenz ist erfolgreich.

Ein einfacheres Beispiel:

private static void GenericMethod<T1>(Func<T1, T1> func) 
{ 
    // ... 
} 

GenericMethod((a) => 1); // CS0411, `a` is not inferred from the body of the method. 

GenericMethod<int>((a) => 1); // compiles 

GenericMethod((int a) => 1); // compiles 
+1

Beachten Sie, dass der Compiler nicht nur wissen muss, dass die Additionsoperation unterstützt wird, sondern dass er den * tatsächlichen Typ * kennen muss, den er für 'T4' verwenden kann. – poke

+0

@poke, um den Rückgabetyp der '+' Methode zu kennen. Recht ? – user3185569

+1

Nicht nur das, sondern auch das Typargument 'T4' aufzulösen. Typargumente müssen zur Kompilierzeit durch "echte" Typen ersetzt werden. Sie können nicht generisch bleiben. – poke

-1

Die Antwort, die ich jetzt sehe, ist, dass der Compiler keine Information hat, dass er die Art von T4 zu bestimmen, verwenden kann. Insbesondere kann es den Ausdruck nicht untersuchen und daraus einen Typ ableiten.

Um es zu beheben, benötigen wir ein Argument, das den Typ von T4 explizit festlegt. Hier ist eine Möglichkeit, es zu tun.

var varf1c = varc.F2(t => new { City = t.City }, 0, (t, v) => v + t.Status, (g, a) => new { City = g.City, Bigstatus = a }); 

public static T1 F2<T2, T3, T4, T1>(
    this T2 s, 
    Func<T2, T3> g, 
    T4 i, 
    Func<T2, T4, T4> a, 
    Func<T3, T4, T1> r) 
    where T2 : class where T3 : class where T1 : class { 
    return null; 
} 

Nun T4 ist mit dem Argumente literal 0 zuzugeordnet, aus dem der Compiler eine Art von int ableiten kann.

+0

Das Ändern der öffentlichen API, nur um einige weitere Typen zuzulassen, ist ein schrecklicher Weg, dies zu lösen. Geben Sie den Typ explizit wie in der Frage an und es geht Ihnen gut. – poke

+0

@poke: Nein, das ist keine Antwort auf die Frage (siehe Bearbeiten). Es ist nicht möglich, Typanmerkungen zu verwenden, wenn anonyme Klassen beteiligt sind. Der Hauptpunkt der Fragen war daher, eine Möglichkeit zu finden, die _function -Deklaration_ so zu ändern, dass Typinterferenzen funktionieren und Typanmerkungen vermieden werden. Offensichtlich hast du es verpasst. –

Verwandte Themen