2010-09-02 1 views
13

Je suis en train de créer une méthode d'extension générique, qui fonctionne sur les tables de données typées:Méthode d'extension générique: argument type ne peut être déduit de l'utilisation

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

La première ligne fonctionne très bien, mais il est vraiment moche ...
La deuxième ligne ne compile pas car le compilateur ne peut pas déduire le type de RowType.
Il s'agit d'une méthode qui sera utilisée en tant que partie d'une couche de données par de nombreux programmeurs différents, donc je préférerais ne pas en avoir besoin pour spécifier le paramètre TypeParameter.
Le compilateur ne devrait-il pas savoir que RowType est du même type que celui qui a été utilisé par TypedTableBase?

Pour différentes raisons qui peuvent ne pas être évidentes dans cet exemple de code, j'ai vraiment besoin de renvoyer le datatable sous sa forme originale. Et la raison pour laquelle j'ai besoin RowType est ainsi le 'Expression < Func < T, bool>>' sera tapé et vu par InteliSence.

Merci

Répondre

19

inférence de type de méthode ne permet pas de déductions d'arguments pour contraintes. Il fait des inférences à partir des arguments à les paramètres formels puis vérifie si les inférences faites à partir des arguments aux formels satisfont les contraintes.

Dans votre cas, les arguments ne permettent pas de déduire quels sont les paramètres de type sans d'abord regarder les contraintes, ce que nous n'allons pas faire jusqu'à ce que nous vérifiions les inférences par rapport aux contraintes. Désolé à ce sujet, mais c'est comme cela que l'algorithme d'inférence de type est spécifié. On m'a posé des questions à ce sujet plusieurs fois et le consensus semble être que je suis moralement mauvais pour maintenir la position que l'inférence devrait déduire des arguments aux seuls paramètres formels. Pour une douzaine de gens qui me disent que je suis obtuse à cet égard, voir les commentaires à mon analyse de cette question étroitement liée:

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

Je maintiens ma position.

+3

J'avais peur de ça ... merci pour la réponse –

+1

Vous avez raison :) – Brian

0

La réponse d'Eric est excellente pour expliquer pourquoi les types ne peuvent pas être déduits. Voici quelques suggestions pour réduire la verbosité du code que vous devrez écrire.

Si vous pouvez définir explicitement le type de votre expression lambda, alors il peut déduire les types.

Voici un exemple de la façon de procéder. J'ai créé un paramètre criteria qui est explicitement de type Expression<Func<MyTypedDataSet.MyTypedRow, bool>>. Dans cet exemple, cela ne vous épargnera pas beaucoup de frappe, mais peut-être qu'en pratique vous pouvez l'utiliser.

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

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

     return table.DoSomething(criteria); 

EDIT: modifier mon exemple d'utiliser une autre méthode d'extension plutôt que de dériver une classe personnalisée TypedTableBase<T> de System.Data.TypedTableBase<T>. Voici un autre exemple qui peut mieux calculer les paramètres de type.Vous définissez une autre méthode d'extension (la mienne s'appelle RowPredicate) qui n'a qu'un seul paramètre de type à déduire. Le premier paramètre est de type TypedTableBase<RowType>, de sorte que le compilateur ne devrait avoir aucun problème déduisant du type de celui:

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

Cela vous permet de compiler le code suivant:

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

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

Principalement le paramètre table simplement les serveurs pour informer le compilateur du type à utiliser pour RowType. Est-ce une bonne idée? Je ne suis pas si sûr, mais cela permet au compilateur d'inférer tous les types génériques.

0

Même si ce n'était pas idéal, je lui ai donné à essayer de retourner quoi que ce soit sorcière me permet de faire quelque chose comme ça:

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 
    } 

Et puis utiliser comme ceci:

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

et le compilateur déduit le type correctement ...

Merci à vous deux pour vos réponses.

Questions connexes