2012-06-27 8 views
12

Por lo tanto, un método bastante común para la extensión IEnumerable, Run:¿Por qué la resolución de sobrecarga de C# no funciona entre Func <T,T> y Acción <T>?

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Action<T> action) 
{ 
    foreach (var item in source) 
    { 
     action(item); 
     yield return item; 
    } 
} 

Cuando trato de usar que con, por ejemplo, DbSet.Add:

invoice.Items.Run(db.InvoiceItems.Add); 
// NB: Add method signature is 
// public T Add(T item) { ... } 

... el compilador se queja de que tiene el tipo de devolución incorrecto, porque está esperando un método vacío. Por lo tanto, añadir una sobrecarga de ejecución que toma un Func lugar de acción:

public static IEnumerable<T> Run<T>(this IEnumerable<T> source, Func<T, T> action) 
{ 
    return source.Select(action).ToList().AsEnumerable(); 
} 

Y ahora el compilador se queja de que "La llamada es ambigua entre los métodos siguientes ..."

Así que mi pregunta es, ¿Cómo puede la sobrecarga de acción del método Run causar ambigüedad cuando no es válida para el grupo de métodos?

+0

¿Cuál es la firma de 'db.InvoiceItems.Add'? – leppie

+0

public T Add (elemento T) {...} –

+0

Respuesta corta: 'x => x.ToString()' ¿debería esta lambda simplemente invocar ToString o invocar ToString y devolver su resultado?En otras palabras, ¿se debería manejar esta lambda como un func o una acción? El compilador no puede tomar esta decisión por ti y, por lo tanto, un error. – Polity

Respuesta

5

Esto ya ha sido explicado por Eric y Jon en las respuestas a this question. Para resumir, así es como funciona el compilador de C#; Precisamente, cuando se trata de la conversión de grupo Método de decidir qué delegado, este se convertirá a utiliza resolución de sobrecarga, que no toma tipos volver en cuenta:

El principio aquí es que la determinación de la convertibilidad grupo método requiere la selección de un método desde un grupo de métodos que usa resolución de sobrecarga, y la resolución de sobrecarga no considera los tipos de devolución.

En su ejemplo compilador ve tanto Action<T> y Func<T, T> como mejor partido de Add. Esto se suma a dos opciones posibles, y dado que requiere una, se emite un error apropiado.

0

sobrecarga try en forma correcta:

public static IEnumerable<TDest> Run<TSource, TDest>(this IEnumerable<TSource> source, 
    Func<TSource, TDest> action) 
{ 
return source.Select(action).ToList(); 
} 
+0

No hace la más mínima diferencia. –

+0

Debe eliminar .ToList aquí, para evitar ejecutar la consulta –

+0

@SteveB El nombre del método es Ejecutar, pero no LazyRun –

0

No puedo responder por qué sino para resolver la ambigüedad se puede convertir explícitamente su función:

invoice.Items.Run((Func<T,T>)db.InvoiceItems.Add); 

o utilizar un lambda

invoice.Items.Run(x => db.InvoiceItems.Add(x)); 
0

No sé por qué no puede resolverlo automáticamente, pero aquí hay dos soluciones:

// with T replaced with the actual type: 
invoice.Items.Run((Func<T, T>)db.InvoiceItems.Add); 
invoice.Items.Run(new Func<T, T>(db.InvoiceItems.Add)); 

¿Por qué necesita estos métodos de todos modos? ¿Qué pasa con:

foreach (var item in invoice.Items) 
    db.InvoiceItems.Add(item); 

La legibilidad de este es mucho mejor. A menos que tenga una buena razón para necesitar el método Run, recomendaría no usarlo. Por lo que he visto, no hay una razón, al menos para la sobrecarga Action<T>.

+0

Ejecutar es una operación declarativa de estilo funcional común, y no estoy de acuerdo con que La forma foreach es más legible. Además, una vez que has colocado los frenos, son cuatro líneas en lugar de una. Estoy haciendo esto para media docena de colecciones de niños; agregue una línea en blanco entre cada uno y eso es ~ 30 líneas de código, lo que hace que el método contenga demasiado tiempo, por lo que refactorizar cada foreach en un método separado. Luego refactorizo ​​ese método para mantener las cosas SECAS y, bueno, tengo un método Run de todos modos. –

+1

@MarkRendle Your 'Run()' no es muy funcional. Gran parte de la programación funcional es escribir funciones que no tienen efectos secundarios. Y 'Run()' es útil * solo * para los efectos secundarios. Estoy de acuerdo con Tim en esto: 'foreach' es más legible y usar métodos como' Run() 'no es una muy buena práctica. – svick

+0

@svick Estoy respetuosamente en desacuerdo. –

Cuestiones relacionadas