2010-09-02 15 views
13

Próbuję stworzyć ogólną metodę rozszerzenia, które działa na wpisywanych tabel danych:Generic rozszerzenie metoda: Typ argumentu nie można wywnioskować z wykorzystaniem

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 
    } 
} 

Pierwsza linia działa dobrze, ale to jest naprawdę brzydki ...
Druga linia nie kompiluje się, ponieważ kompilator nie może wywnioskować typu RowType.
Jest to metoda, która będzie używana jako część DataLayer przez wielu różnych programistów, więc raczej nie będę ich potrzebował do określenia parametru TypeParameter.
Czy kompilator nie powinien wiedzieć, że RowType jest tym samym typem, który został użyty przez TypedTableBase?

Z różnych powodów, które mogą nie być oczywiste w tej próbce kodu, naprawdę muszę zwrócić datatable w pierwotnej formie. A powodem, dla którego potrzebuję RowType, jest wyrażenie "Expression < Func < T, bool>>" i zostanie wyświetlone przez InteliSence.

Dzięki

Odpowiedz

19

typu metoda wnioskowania nie wnioskować z argumentów ograniczeń. Wyciąga on z argumentów na formalne parametry , a następnie sprawdza, czy wnioski wyciągnięte z argumentów do formaliów spełniają ograniczenia.

W twoim przypadku nie ma wystarczającej ilości danych z argumentów, aby wywnioskować, jakie są parametry typu bez uprzedniego spojrzenia na ograniczenia, które nie zamierzamy wykonywać, dopóki nie sprawdzimy wniosków pod kątem ograniczeń. Przepraszam za to, ale w ten sposób określono algorytm wnioskowania o typie.

Wiele razy zadawano mi pytania na ten temat, a konsensus wydaje się, że jestem moralnie niesłuszny za utrzymanie stanowiska, które wnioskowanie powinno wywodzić z argumentów wyłącznie na parametry formalne. Około tuzina osób mówi mi, że jestem uparty w tym względzie, patrz komentarze do mojej analizy tego ściśle związane numerze:

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

mi utrzymać moją pozycję.

+3

Bałem się tego ... dzięki za odpowiedź –

+1

Masz rację :) – Brian

0

Odpowiedź Erika jest świetna do wyjaśnienia, dlaczego typów nie można wywnioskować. Oto kilka sugestii, aby, na szczęście, ograniczyć szczegółowość kodu, który będziesz musiał napisać.

Jeśli możesz jednoznacznie zdefiniować typ wyrażenia lambda, to możesz określić typy.

Przykład tego, jak to zrobić, znajduje się poniżej. Stworzyłem parametr criteria jawnie typu Expression<Func<MyTypedDataSet.MyTypedRow, bool>>. W tym przykładzie nie oszczędza to pisania na klawiaturze, ale być może w praktyce można z tego skorzystać.

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

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

     return table.DoSomething(criteria); 

EDIT: zmieniony moim przykładem jest użycie innej metody rozszerzenie zamiast wyprowadzania niestandardową TypedTableBase<T> klasę od System.Data.TypedTableBase<T>.

Poniżej znajduje się kolejny przykład, który może lepiej wywnioskować parametry typu.Użytkownik definiuje inną metodę rozszerzenia (moja nazwa nazywa się RowPredicate), która ma tylko jeden parametr do wyprowadzenia. Pierwszy parametr jest typu TypedTableBase<RowType>, więc kompilator nie powinien mieć problemu wywodząc typ od tego:

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

To pozwala skompilować następujący kod:

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

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

głównie table parametrów prostu serwerów aby poinformować kompilator typu, którego należy użyć dla RowType. Czy to dobry pomysł? Nie jestem tego taki pewien, ale pozwala on kompilatorowi wywnioskować wszystkie typy ogólne.

0

Nawet jeśli to nie było idealne, dałem się próbuje powrócić cokolwiek czarownica pozwala mi zrobić coś takiego:

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 
    } 

a następnie używać go tak:

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

i kompilator poprawnie podaje typ ...

Dziękuję wam za odpowiedzi.

Powiązane problemy