2010-09-02 7 views
13

Ich versuche, eine generische Erweiterungsmethode zu erstellen, die auf typisierten Datentabellen funktionieren:Generika-Extension-Methode: Typ Argument kann nicht aus der Nutzung abgeleitet wird

public static class Extensions 
{ 
    public static TableType DoSomething<TableType, RowType>(this TableType table, param Expression<Func<RowType, bool>>[] predicates) 
     where TableType : TypedTableBase<RowType> 
     where RowType : DataRow 
    { 
     // do something to each row of the table where the row matches the predicates 
     return table; 
    } 

    [STAThread] 
    public static void main() 
    { 
     MyTypedDataSet.MyTypedDataTable table = getDefaultTable(); 
    } 

    public static MyTypedDataSet.MyTypedDataTable getDefaultTable() 
    { 
     // this line compiles fine and does what I want: 
     return new MyTypedDataSet.MyTypedDataTable().DoSomething<MyTypedDataSet.MyTypedDataTable, MyTypedDataSet.MyTypedRow>(row => row.Field1 == "foo"); 

     // this line doesn't compile : 
     return new MyTypedDataSet.MyTypedDataTable().DoSomething(row => row.Field1 == "foo"); 
     // Error : The type arguments .. cannot be inferred from the usage 
    } 
} 

Die erste Zeile funktioniert gut, aber es ist wirklich hässlich ...
Die zweite Zeile kompiliert nicht, da der Compiler den Typ RowType nicht ableiten kann.
Dies ist eine Methode, die von vielen verschiedenen Programmierern als Teil eines DataLayers verwendet wird. Daher brauche ich sie nicht, um den TypeParameter anzugeben.
Sollte der Compiler nicht wissen, dass RowType der gleiche Typ ist, der von TypedTableBase verwendet wurde?

Aus verschiedenen Gründen, die in diesem Codebeispiel möglicherweise nicht offensichtlich sind, muss ich die Datentabelle in seiner ursprünglichen Form wirklich zurückgeben. Und der Grund, warum ich brauche RowType ist so die '< Func < T, bool>>' wird getippt und von InteliSence gesehen werden.

Dank

Antwort

19

Methode Typinferenz machen keine Schlüsse aus Argumenten Zwänge. Es führt Rückschlüsse von Argumenten auf Formalparameter und dann überprüft, ob die Schlussfolgerungen aus den Argumenten für die Formals die Bedingungen erfüllen.

In Ihrem Fall ist es nicht genug Daten aus den Argumenten abzuleiten, was die Parameter des Typs sind, ohne zuerst auf den Zwängen der Suche, die wir nicht tun zu gehen, bis wir die Schlüsse gegen die Zwänge überprüfen. Entschuldigung, aber so ist der Typ-Inferenz-Algorithmus spezifiziert.

Ich habe viele Fragen dazu gestellt und der Konsens scheint zu sein, dass ich moralisch falsch bin für die Aufrechterhaltung der Position, die Inferenz von Argumenten zu formalen Parametern allein herleiten sollte. Für etwa ein Dutzend Leute sagen mir, ich bin in dieser Hinsicht verbohrt, siehe die Kommentare zu meiner Analyse dieser eng verwandten Thema:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

ich meine Position zu halten.

+3

Ich hatte Angst davor ... danke für die Antwort –

+1

Sie haben Recht :) – Brian

0

Erics Antwort ist großartig, um zu erklären, warum die Typen nicht abgeleitet werden können. Hier sind ein paar Vorschläge, um hoffentlich die Ausführlichkeit des Codes zu reduzieren, den Sie schreiben müssen.

Wenn Sie den Typ Ihres Lambda-Ausdrucks explizit definieren können, kann er die Typen ableiten.

Ein Beispiel dafür ist unten. Ich habe einen criteria Parameter erstellt, der explizit vom Typ Expression<Func<MyTypedDataSet.MyTypedRow, bool>> ist. In diesem Beispiel sparen Sie nicht viel Tipparbeit, aber vielleicht können Sie dies in der Praxis nutzen.

 MyTypedDataSet.MyTypedDataTable table = new MyTypedDataSet.MyTypedDataTable(); 

     Expression<Func<MyTypedDataSet.MyTypedRow, bool>> criteria = row => row.Field1 == "foo"; 

     return table.DoSomething(criteria); 

EDIT: veränderte mein Beispiel eher eine andere Erweiterungsmethode zu verwenden, als eine benutzerdefinierte TypedTableBase<T> Klasse von System.Data.TypedTableBase<T> abzuleiten.

Unten ist ein weiteres Beispiel, das die Typparameter besser ableiten kann.Sie definieren eine andere Erweiterungsmethode (meine heißt RowPredicate), die nur einen Typparameter zum Ableiten hat. Der erste Parameter ist vom Typ TypedTableBase<RowType>, so dass der Compiler sollte kein Problem haben, die Art von dem Folgern:

public static Expression<Func<RowType, bool>> RowPredicate<RowType>(this TypedTableBase<RowType> table, Expression<Func<RowType, bool>> predicate) 
     where RowType : DataRow 
    { 
     return predicate; 
    } 

Auf diese Weise können Sie den folgenden Code kompilieren:

 MyTypedDataSet.MyTypedDataTable table = new MyTypedDataSet.MyTypedDataTable(); 

     return table.DoSomething(table.RowPredicate(row => row.Field1 == "foo")); 

Vor allem die table Parameter einfach Server Informieren Sie den Compiler über den Typ, der für RowType verwendet werden soll. Ist das eine gute Idee? Ich bin mir nicht so sicher, aber es erlaubt dem Compiler, auf alle generischen Typen zu schließen.

0

Auch wenn das nicht ideal war, gab ich zu jeder Hexe versucht, bis Rückkehr alles erlaubt es mir, so etwas zu tun:

public static void DoSomething<RowType>(this TypedTableBase<RowType> table, param Expression<Func<RowType, bool>>[] predicates) 
    where RowType : DataRow 
    { 
     // do something to each row of the table where the row matches the predicates 
     // do not return the table... too bad for chaining commands 
    } 

Und es dann wie verwenden so:

MyTypedDataSet.MyTypedDataTable table = new MyTypedDataSet.MyTypedDataTable(); 
table.DoSomething(row => row.Field1 == "foo")); 

und der Compiler leitet den Typ korrekt ein ...

Vielen Dank für Ihre Antworten.

Verwandte Themen