2010-09-02 8 views
13

Estoy tratando de crear un método de extensión genérica, que funciona en las tablas de datos proporcionado:extensión genérica método: argumento de tipo no se puede deducir de la utilización

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 primera línea funciona bien, pero es muy feo ...
La segunda línea no se compila porque el compilador no puede inferir el tipo de RowType.
Este es un método que muchos programadores diferentes utilizarán como parte de un DataLayer, por lo que preferiría no necesitar que especifiquen el TypeParameter.
¿No debería el compilador saber que RowType es del mismo tipo que el utilizado por TypedTableBase?

Por diferentes razones que pueden no ser obvias en este ejemplo de código, realmente necesito devolver la tabla de datos en su forma original. Y la razón por la que necesito RowType es por lo que la 'Expresión < Func < T, bool>>' será escrita y vista por InteliSence.

Gracias

Respuesta

19

tipo de método de inferencia no hace inferencias a partir de argumentos para limitaciones. Realiza inferencias de argumentos a parámetros formales y luego verifica si las inferencias hechas desde los argumentos a los formales satisfacen las restricciones.

En su caso, no hay suficientes datos de los argumentos para deducir cuáles son los parámetros de tipo sin primero mirar las restricciones, que no vamos a hacer hasta que verifiquemos las inferencias contra las restricciones. Perdón por eso, pero así es como se especifica el algoritmo de inferencia tipo.

Me han hecho preguntas sobre esto muchas veces y el consenso parece ser que soy moralmente incorrecto para mantener la posición que la inferencia debe inferir de los argumentos a los parámetros formales solamente. Por alrededor de una docena de personas que me dicen que estoy equivocada en este sentido, véanse los comentarios a mi análisis de esta cuestión estrechamente relacionada:

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

mantengo mi posición.

+3

Tenía miedo de eso ... gracias por la respuesta –

+1

Tienes razón :) – Brian

0

La respuesta de Eric es excelente para explicar por qué no se pueden inferir los tipos. Aquí hay un par de sugerencias para reducir la verbosidad del código que tendrá que escribir.

Si puede definir explícitamente el tipo de su expresión lambda, entonces puede inferir los tipos.

Un ejemplo de cómo hacerlo está a continuación. Creé un parámetro criteria que es explícitamente del tipo Expression<Func<MyTypedDataSet.MyTypedRow, bool>>. En este ejemplo, esto no te ahorra mucho tipeo, pero tal vez en la práctica puedas hacer uso de esto.

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

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

     return table.DoSomething(criteria); 

EDIT: altera mi ejemplo utilizar otro método de extensión en lugar de derivar una clase personalizada TypedTableBase<T> de System.Data.TypedTableBase<T>.

A continuación se muestra otro ejemplo que puede hacer un mejor trabajo al inferir los parámetros de tipo.Usted define otro método de extensión (el mío se llama RowPredicate) que solo tiene un parámetro de tipo para inferir. El primer parámetro es de tipo TypedTableBase<RowType>, por lo que el compilador no debería tener problemas inferir el tipo de que:

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

Esto le permite compilar el código siguiente:

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

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

Principalmente los table parámetros simples servidores para informar al compilador del tipo que se utilizará para RowType. ¿Es esta una buena idea? No estoy tan seguro, pero permite que el compilador infiera todos los tipos genéricos.

0

Incluso si eso no era ideal, que dejó de intentar devolver nada en absoluto bruja me permite hacer algo así:

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 
    } 

y luego usarlo de esta manera:

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

y el compilador infiere el tipo correctamente ...

Gracias a ambos por sus respuestas.

Cuestiones relacionadas